1. 约瑟夫环问题
链表: 链表是一组数据项的集合,其中每个数据项都是一个节点的一部分,每个节点都包含指向下一个节点的链接。
此处的约瑟夫环解决方案用于说明,链表是一种抽线,而链表的指针实现是一种具体实现,我们同样可以用索引的方式实现链表。
链表: 链表是一组数据项的集合,其中每个数据项都是一个节点的一部分,每个节点都包含指向下一个节点的链接。
此处的约瑟夫环解决方案用于说明,链表是一种抽线,而链表的指针实现是一种具体实现,我们同样可以用索引的方式实现链表。
a. 单链表声明(.h文件):
/*单链表*/ typedef struct node * link; struct node{ int item; link next; };
b. 单链表解决方案:
/*************************************************************** 约瑟夫环问题: n个人围成一圈,从1编号到n,沿着这个圈每次数M个人就排除对应者,每当有人出列后,剩下的人靠拢,仍然保持一个完整的圆周,找出最后 剩下的人 ****************************************************************/ int josephus_circle(int n,int m) { assert(n>1); assert(m >= 1); link head; head = (link)malloc(n*sizeof(*head)); assert(head != NULL); for (int i = 0; i < n; i++) { head[i].item = i + 1; head[i].next = &head[i + 1]; } head[n - 1].next = &head[0]; link x = &head[n - 1]; while (x != x->next) { for (int i = 1; i < m; i++) { x = x->next; } x->next = x->next->next; } int rlt = x->item; free(head); return rlt; }
c. 索引解决方案:
/*************************************************************** 约瑟夫环问题: 用索引模拟链表 ****************************************************************/ int josephus_circle2(int n,int m) { typedef struct _element { int item; int next; }josephus_element; assert(n>1); assert(m >= 1); josephus_element *head; head = (josephus_element *)malloc(n*sizeof(josephus_element)); assert(head != NULL); for (int i = 0; i < n; i++) { head[i].item = i + 1; head[i].next = i + 1; } head[n - 1].next = 0; int x = n - 1; while (x != head[x].next) { for (int i = 1; i < m; i++) { x = head[x].next; } head[x].next = head[(head[x].next)].next; } int rlt = head[x].item; free(head); return rlt; }
2. 单链表倒置
/**************************************************************** 单链表倒置 ****************************************************************/ link list_reverse(link x) { assert(NULL != x); link t,y = x,r = NULL; while (y != NULL) { t = y->next; y->next = r; r = y; y = t; } return r; }
3. 邻近点对计算
a. 基本方法:
/**************************************************************** 邻近点对问题: 对于N个随机产生的单位正方形中的点,统计可以被长度小于d的直线连接 的点的对数。 ****************************************************************/ typedef struct{ double x,y; }point; static double point_distance(point a,point b) { double x = a.x - b.x,y = a.y - b.y; return x*x + y*y; } /**************************************************************** 方案一: 复杂度: O(N*N) ****************************************************************/ int near_point2(int n,double d,double *randdata) { assert(n > 1); assert(d > 0 && d < 1.0); assert(randdata != NULL); point *a = (point *)malloc(n * sizeof(point)); assert(NULL != a); int cnt = 0; int index = 0; for (int i = 0; i < n; i++) { a[i].x = randdata[index++]; a[i].y = randdata[index++]; } double d2 = d*d; for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { if (point_distance(a[i],a[j]) < d2) cnt++; } } free(a); return cnt; }
b. 网格算法
typedef struct _node* _link; struct _node{ point p; _link next; }; /**************************************************************** 二维数组的申请: 注意此种方法,用此方法可以在代码中使用类似a[i][j]的表达式 ****************************************************************/ static _link** malloc2d(int m,int n) { _link** t = (_link**)malloc(m * sizeof(*t)); assert(t != NULL); for (int i = 0; i < m; i++) { t[i] = (_link *)malloc(n * sizeof(*(t[i]))); assert(t[i] != NULL); } return t; } static void free2d(_link** t,int n) { for (int i = 0; i < n; i++) { free(t[i]); } free(t); } /**************************************************************** 将点放入网格中: 针对这个应用,可以将统计邻近点的动作也放入此函数中 ****************************************************************/ #define INSERT_AND_COUNT 0 #if INSERT_AND_COUNT static void grid_insert(_link** grid,_link node,int factor,int *cnt,double d) { int X = (int)(node->p.x * factor) + 1; int Y = (int)(node->p.y * factor) + 1; // 当x或者y为1.0时,应该减1 if (X == factor + 1) X -= 1; if (Y == factor + 1) Y -= 1; for (int i = X - 1; i <= X + 1; i++) { for (int j = Y - 1; j <= Y + 1; j++) { for (_link s = grid[i][j]; s != NULL; s = s->next) { if (point_distance(node->p,s->p) < d) (*cnt)++; } } } node->next = grid[X][Y]; grid[X][Y] = node; } #else static void grid_insert(_link** grid,int factor) { int X = (int)(node->p.x * factor) + 1; int Y = (int)(node->p.y * factor) + 1; // 当x或者y为1.0时,应该减1 if (X == factor + 1) X -= 1; if (Y == factor + 1) Y -= 1; node->next = grid[X][Y]; grid[X][Y] = node; } #endif /**************************************************************** 将单位正方形划分为大小相等的较小的正方形网格。然后对每个正方形网格, 为所有其中的点生成一个链表。通过二维素族,可以立即访问已知点附近的 点集合。 空间复杂度: O(1/d*d + N) 时间复杂度: O(d*d*N*N) 对于小的d值,本算法执行很快 ****************************************************************/ int near_point(int n,double *randdata) { int G = (int)(1 / d); // 生成网格 _link** grid = malloc2d(G + 2,G + 2); // 周边加哨兵 for (int i = 0; i < G + 2; i++) { for (int j = 0; j < G + 2; j++) { grid[i][j] = NULL; } } _link head = (_link)malloc(n*sizeof(*head)); assert(head != NULL); double d2 = d*d; int cnt = 0; // 撒点(并计算) int index = 0; for (int i = 0; i < n; i++) { head[i].p.x = randdata[index++]; head[i].p.y = randdata[index++]; #if INSERT_AND_COUNT grid_insert(grid,&head[i],G,&cnt,d2); #else grid_insert(grid,G); #endif } #if (!INSERT_AND_COUNT) for (int i = 1; i < G + 1; i++) // 不需要考虑哨兵,因此只到G+1 { for (int j = 1; j < G + 1; j++) { for (_link cur = grid[i][j]; cur != NULL; cur = cur->next) { // 当前网格链表中的其它元素 for (_link tmp = cur->next; tmp != NULL; tmp = tmp->next) { if (point_distance(cur->p,tmp->p) < d2) cnt++; } // 右边网格链表 for (_link tmp = grid[i][j + 1]; tmp != NULL; tmp = tmp->next) { if (point_distance(cur->p,tmp->p) < d2) cnt++; } // 左下网格链表 for (_link tmp = grid[i + 1][j - 1]; tmp != NULL; tmp = tmp->next) { if (point_distance(cur->p,tmp->p) < d2) cnt++; } // 下方网格链表 for (_link tmp = grid[i + 1][j]; tmp != NULL; tmp = tmp->next) { if (point_distance(cur->p,tmp->p) < d2) cnt++; } // 右下方网格链表 for (_link tmp = grid[i + 1][j + 1]; tmp != NULL; tmp = tmp->next) { if (point_distance(cur->p,tmp->p) < d2) cnt++; } } } } #endif free(head); free2d(grid,G + 2); return cnt; }
其中使用INSERT_AND_COUNT宏控制是否在插入时计算,经实测试,在插入时计算要稍快一些。