1. 试写一算法,实现线性表就地逆置 (1)顺序表,利用原表的存储空间将线性表(a1,a2,…,an)逆置为(an,an-1,…,a1); (2)单链表,带头结点。 (1)该算法要求利用原表的存储空间将线性表逆置,也就是不能再新建一个表将元素插入其中,因此只需交换顺序表的第一个和倒数第一个元素、第二个和倒数第二个元素…以此类推,就可以逆置顺序表。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 typedef struct { ElemType *data; int length; } SqList; Status CreateSqList (SqList &L, int n) { ElemType *p; L.data = (ElemType *)malloc (n * sizeof (ElemType)); if (!L.data) printf ("\n分配失败\n" ); printf ("请依次输入顺序表的数据元素:\n" ); for (p=L.data; p< L.data+n; p++) scanf ("%d " , p); L.length = n; if (L.length) printf ("顺序表创建成功,它的长度为:%d\n" , L.length); return OK; } Status SqListReverse (SqList &L) { int i; ElemType x; for (i=0 ; i< L.length/2 ; i++) { x = L.data[i]; L.data[i] = L.data[L.length-i-1 ]; L.data[L.length-i-1 ] = x; } return OK; } int main () { SqList L; CreateSqList(L, 10 ); SqListReverse(L); return OK; }
(2)带头结点的单链表原地逆置只需将单链表每个结点中的next指针改为指向其前一个结点,即原来的第一个结点变为最后一个结点,next指针为空,原来的最后一个结点变为第一个结点,原来的头结点指向原来的最后一个结点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 typedef struct LNode { ElemType data; struct LNode *next ; } LNode, *LinkList; LinkList List_HeadInsert (LinkList &L) { LNode *s; int x; L = (LinkList *) malloc (sizeof (LNode)); L->next = NULL ; scanf ("%d" , &x); while (x != 9999 ) { s = (LNode *) malloc (sizeof (LNode)); s->data = x; s->next = L->next; L->next = s; scanf ("%d" , &x); } return L; } Status LinkListReverse (Linklist &L) { LNode *pre = L, *p = L->next, *r = p->next; p->next = NULL ; while (r != NULL ) { pre = p; p = r; r = r->next; p->next = pre; } L->next = p; return OK; } int main () { LinkList L; List_HeadInsert(L); LinkListReverse(L); return OK; }
2. 请利用两个栈来模拟一个队列(可以不考虑栈满情况)。 已知栈的三个运算定义如下: int S_Push(Stack &s,ElemType x);//元素 x 入栈; int S_Pop(Stack &s,ElemType &x);//栈顶元素出栈,赋给变量 x; int S_isEmpty(Stack s);//判栈是否为空。 利用栈的运算来实现该队列的三个运算: int Q_En(Stack & s1,Stack & s2,ElemType x);//插入一个元素入队列; int Q_De(Stack & s1,Stack & s2,ElemType &x);//删除一个元素出队列; int Q_isEmpty(Stack s1,Stack s2);//判队列是否为空。
栈的特点是后进先出,队列的特点是先进先出。用两个栈S1和S2模拟队列时,令S1执行入栈操作模拟入队,S2执行出栈操作模拟出队。本题无需考虑栈满情况。 入队时,将元素直接插入S1中。 出队时,若S2不空,则将S2栈顶元素出栈;若S2为空且S1为空,则队列中没有元素,无法执行出队操作;若S2为空且S1不空,则先将S1中元素全部逐一转入S2,最后将S2栈顶元素出栈。 判空条件为S1和S2都为空时,则队列为空。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 #define MaxSize 50 typedef struct { ElemType data[MaxSize]; int top=-1 ; } Stack; int Q_En (Stack & s1, Stack & s2, ElemType x) { Push(s1, x); return 1 ; } int Q_De (Stack & s1, Stack & s2, ElemType &x) { if (!S_isEmpty(s2)) S_Pop(s2, x); else if (S_isEmpty(s1)) { printf ("队列为空\n" ); return 0 ; } else { while (!S_isEmpty(s1)) { S_Pop(s1, x); S_Push(s2, x); } S_Pop(s2, x); } return 1 ; } int Q_isEmpty (Stack s1,Stack s2) { if (S_isEmpty(S1)&&S_isEmpty(S2)) return 1 ; else return 0 ; }
3. 试写一算法,判断两个字符串是否可以循环匹配。例如:s1=”abcaaaab”和 s2=”aaaababc”匹配。
若两个字符串可以循环匹配,则两个字符串必须长度相同,且在字符串s3=s1+s1中一定可以找到字符串s2。在字符串s3中寻找s2时使用KMP算法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #define MaxLen 255 typedef struct { char ch[MaxLen]; int length; }SString; void get_next (SString T,int next[]) { int i=1 ,j=0 ; next[1 ]=0 ; while (i<T.length){ if (j==0 || T.ch[i]==T.ch[j]){ ++i; ++j; next[i]=j; } else j=next[j]; } } int Index_KMP (SString S,SString T,int next[]) { int i=1 ,j=1 ; while (i<=S.length && j<=T.length){ if (j==0 || S.ch[i]==T.ch[j]){ ++i; ++j; } else j=next[j]; } if (j>T.length) return i-T.length; else return 0 ; } int SStringCycleMatch (SString s1, SString s2) { if (s1.length!=s2.length) return 0 ; SString s3; strcat (s3.ch,s1.ch);strcat (s3.ch,s1.ch); if (Index_KMP(s3,s2,&next[0 ])) return 1 ; else return 0 ; } int main () { SString s1,s2; gets(s1.ch); s1.length = strlen (s1.ch); gets(s2.ch); s2.length = strlen (s2.ch); get_next(s2,next); if (SStringCycleMatch(s1,s2)==1 ) printf ("可以循环匹配\n" ); else printf ("不可以循环匹配\n" ); return 0 ; }
4. 以三元组顺序表存储稀疏矩阵,实现两个矩阵相乘。
稀疏矩阵中每一个元素都是一个三元组,有行数row,列数col,以及数据域value。 两个稀疏矩阵相乘时,将第一个矩阵的第一个三元组和第二个矩阵的所有三元组进行匹配,若第一个三元组的列数和第二个矩阵中的一个三元组的行数相同,那么这两个元素可以相乘,得到结果矩阵的第一个三元组;接着将第一个矩阵的第二个三元组和第二个矩阵的所有三元组进行匹配,若第一个矩阵的第二个三元组的列数和第二个矩阵中的一个三元组的行数相同,那么这两个元素可以相乘,得到结果矩阵的第二个三元组…以此类推,直到第一个矩阵的所有三元组遍历完成,就得到了整个结果矩阵。 最后将结果矩阵进行重新排序,按照行数优先,列数次优先的原则。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 typedef struct { int row; int col; ElemType value; } Triple; typedef struct { Triple *data; int row_num; int col_num; int non_zero; } TSMatrix; void MultSMatrix (TSMatrix *matrix1, TSMatrix *matrix2, TSMatrix *result) { int i = 0 ; int j = 0 ; int k = 0 ; int q; if (matrix1->cols != matrix2->rows) printf ("这两个矩阵无法相乘\n" ); else { result->data = (Triple *) malloc (sizeof (Triple) * (matrix1->rows * matrix2->cols)); result->rows = matrix1->rows; result->cols = matrix2->cols; for (k = 0 ; k < matrix1->rows * matrix2->cols; k++) (result->data + k)->value = 0 ; k = 0 ; while (i < matrix1->NoZero) { for (j = 0 ; j < matrix1->NoZero; j++) if ((matrix1->data + i)->col == (matrix2->data + j)->row) { (result->data + k)->row = (matrix1->data + i)->row; (result->data + k)->col = (matrix2->data + j)->col; (result->data + k)->value = (matrix1->data + i)->value * (matrix2->data + j)->value; for (q = 0 ; q < k; q++) if ((result->data + k)->row == (result->data + q)->row && (result->data + k)->col == (result->data + q)->col) { (result->data + q)->value += (result->data + k)->value; k--; } k++; } i++; } result->NoZero = k; result->data = (Triple *) realloc (result->data, sizeof (Triple) * (result->NoZero)); } } int main () { TSMatrix MatrixA, MatrixB, Result; printf ("请输入第一个矩阵的行数 列数 非零元素数:\n" ); scanf ("%d %d %d" , &MatrixA.rows, &MatrixA.cols, &MatrixA.NoZero); MatrixA.data = (Triple *) malloc (sizeof (Triple) * MatrixA.NoZero); for (int i = 0 ; i < MatrixA.NoZero; ++i) { printf ("请输入第一个矩阵的第%d个三元组:\n" , i + 1 ); scanf ("%d %d %d" , &MatrixA.data[i].row, &MatrixA.data[i].col, &MatrixA.data[i].value); } printf ("请输入第二个矩阵的行数 列数 非零元素数:\n" ); scanf ("%d %d %d" , &MatrixB.rows, &MatrixB.cols, &MatrixB.NoZero); MatrixB.data = (Triple *) malloc (sizeof (Triple) * MatrixB.NoZero); for (int i = 0 ; i < MatrixB.NoZero; ++i) { printf ("请输入第二个矩阵的第%d个三元组:\n" , i + 1 ); scanf ("%d %d %d" , &MatrixB.data[i].row, &MatrixB.data[i].col, &MatrixB.data[i].value); } MultSMatrix(&MatrixA, &MatrixB, &Result); printf ("结果矩阵为:\n" ); for (int i = 0 ; i < Result.NoZero; ++i) printf ("(%d,%d,%d)\n" , Result.data[i].row, Result.data[i].col, Result.data[i].value); }
5. 设计求某个结点在二叉树中的双亲结点算法。
假设二叉树b采用链式存储结构,使用递归求指定值为x的结点的双亲结点,若当前结点的左孩子或右孩子的值与x相等,则返回当前节点的值,否则继续比较当前结点的左右孩子的左右孩子的值与x是否相等,直到找到为止,否则返回NULL。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 typedef struct BiTNode { ElemType data; struct BiTNode *lchild ,*rchild ; }BiTNode, *BiTree; int FindParent (BiTree T,Elemtype x) { if (T){ if ((T->lchild)&&T->lchild->data==x) { printf ("x的双亲结点为%c\n" , T->data); return 1 ; } if ((T->rchild)&&T->rchild->data==x) { printf ("x的双亲结点为%c\n" , T->data); return 1 ; } else { FindParent(T->lchild,x); FindParent(T->rchild,x); } } return 0 ; }
6. 设计一个利用广度优先遍历实现的有向图拓扑排序算法。
因为拓扑排序需要用到入度,因此先求所有顶点的入度。 拓扑排序序列的第一个点一定是入度为0的顶点,因为没有任何边指向它。接下来的点应该是在入度为0的顶点之后,所以它们只能被前面的顶点所指,把刚才那些顶点所指的点的入度减一,如果减之后有入度为0的顶点就把它加入序列。再不断重复上一步操作,拓扑排序就完成了。用一个栈存储入度为0的顶点,如果最后出栈的顶点数小于总顶点数,则说明有环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 typedef struct node //边表结点{ int adjvex; struct node *next ; } eNode; typedef struct headnode //顶点表结点{ int in; char vertex; eNode *firstedge; } hNode; typedef struct //邻接表(图){ hNode adjlist[Max]; int n, e; } linkG; linkG *creat (linkG *g) { int i, k; eNode *s; int n1, e1; char ch; g = (linkG *) malloc (sizeof (linkG)); printf ("请输入顶点数和边数:" ); scanf ("%d%d" , &n1, &e1); g->n = n1; g->e = e1; printf ("顶点数:%d 边数:%d\n" , g->n, g->e); printf ("请输入顶点信息(字母):" ); getchar(); for (i = 0 ; i < n1; i++) { scanf ("%c" , &ch); g->adjlist[i].vertex = ch; g->adjlist[i].firstedge = NULL ; } printf ("\n打印顶点下标及顶点数据:\n" ); for (i = 0 ; i < g->n; i++) { printf ("顶点下标:%d 顶点数据:%c\n" , i, g->adjlist[i].vertex); } getchar(); int i1, j1; for (k = 0 ; k < e1; k++) { printf ("请输入边<i,j>:" ); scanf ("%d %d" , &i1, &j1); s = (eNode *) malloc (sizeof (eNode)); s->adjvex = j1; s->next = g->adjlist[i1].firstedge; g->adjlist[i1].firstedge = s; } return g; } void inDegree (linkG *g) { eNode *p; int i; for (i = 0 ; i < g->n; i++) g->adjlist[i].in = 0 ; for (i = 0 ; i < g->n; i++) { p = g->adjlist[i].firstedge; while (p != NULL ) { g->adjlist[p->adjvex].in++; p = p->next; } } } void TopologicalOrder (linkG *g) { eNode *p; int i, k, gettop; int top = 0 ; int count = 0 ; int *stack = (int *) malloc (g->n * sizeof (int )); for (i = 0 ; i < g->n; i++) { if (g->adjlist[i].in == 0 ) stack [++top] = i; } while (top != 0 ) { gettop = stack [top--]; printf ("%c " , g->adjlist[gettop].vertex); count++; p = g->adjlist[gettop].firstedge; while (p) { k = p->adjvex; g->adjlist[k].in--; if (g->adjlist[k].in == 0 ) { stack [++top] = k; } p = p->next; } } if (count < g->n) printf ("\n有环\n" ); free (stack ); } int main () { linkG *g = NULL ; g = creat(g); inDegree(g); TopologicalOrder(g); return 0 ; }
7. 设计算法实现利用二叉排序树进行排序。
对于二叉排序树的任何一个非叶子结点,要求左子结点的值比当前节点的值小,右子结点的值比当前节点的值大,如果有相同的值,可以将该节点放在左子结点或右子结点。 因此总体思路是先将要排序的数构建成一个二叉排序树,再对该二叉排序树进行中序遍历即可得到按升序排列的数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 typedef struct BiTNode { int data; struct BiTNode *lchild , *rchild ; } BiTNode, *BiTree; typedef struct { BiTree *base; BiTree *top; int stacksize; } Sqstack; #define STACK_INIT_SIZE 100 void InitStack (Sqstack &S) { S.base = (BiTree *) malloc (STACK_INIT_SIZE * sizeof (BiTNode)); if (!S.base) exit (0 ); S.top = S.base; S.stacksize = STACK_INIT_SIZE; } void SearchBST (BiTree T, int key, BiTree f, BiTree &p) { if (!T) p = f; else if (T->data == key) p = T; else if (T->data < key) SearchBST(T->rchild, key, T, p); else SearchBST(T->lchild, key, T, p); } void CreatBST (BiTree &T, int e) { BiTree p, s, q; SearchBST(T, e, NULL , p); s = (BiTree) malloc (sizeof (BiTNode)); s->data = e; s->lchild = NULL ; s->rchild = NULL ; if (!p)T = s; else if (e == p->data) { q = p->rchild; p->rchild = s; s->rchild = q; } else if (e < p->data) p->lchild = s; else p->rchild = s; } #define STACK_INCREMENT 10 void InOrderTravese (BiTree T) { BiTree p; Sqstack Sqt; InitStack(Sqt); p = T; while (p || Sqt.base != Sqt.top) { if (p) { if (Sqt.top - Sqt.base >= Sqt.stacksize) { Sqt.base = (BiTree *) realloc (Sqt.base, (Sqt.stacksize + STACK_INCREMENT) * sizeof (BiTNode)); if (!Sqt.base) exit (OVERFLOW); Sqt.top = Sqt.base + Sqt.stacksize; Sqt.stacksize += STACK_INCREMENT; } *Sqt.top++ = p; p = p->lchild; } else { p = *--Sqt.top; printf ("%d " , p->data); p = p->rchild; } } printf ("\n" ); } #define MAXSIZE 50 int main () { int length; int bst[MAXSIZE]; BiTree BST; BST = (BiTree) malloc (sizeof (BiTNode)); BST = NULL ; printf ("请输入待排序序列的个数N (N<%d):" , MAXSIZE); scanf ("%d" , &length); printf ("请输入待排序的值:\n" ); for (int i = 0 ; i < length; i++) scanf ("%d" , &bst[i]); for (int i = 0 ; i < length; i++) CreatBST(BST, bst[i]); printf ("中序遍历二叉排序树:\n" ); InOrderTravese(BST); }
8. 设计算法实现小顶堆的插入,设关键字序列(k1,k2,…,kn-1)是堆,设计算法将关键字序列(k1,k2,…,kn-1,x)调整为堆。
小顶堆就是一棵完全二叉树,非叶子结点的值不大于左孩子和右孩子的值。 插入x时,先将x插入到二叉树的最后一个结点,再根据小顶堆的定义,自底而上递归调整x的位置。 使用数组实现小顶堆。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 //小顶堆的插入: void MinHeapInsert(int array[], int n, int x) {//n为数组原长 array[n + 1] = x; //先把x插入到二叉树最后一个结点 int a = n + 1; //指向x的角标 int b; while (a != 1) { if (array[a] < array[a / 2]) { //如果x比双亲结点小,就交换x和双亲结点 b = array[a]; array[a] = array[a / 2]; array[a / 2] = b; a /= 2; //指向x的角标减半 } else break; } } //主函数: int main() { int n; printf("请输入元素k的个数:\n");scanf("%d",&n); int MinHeap[n+2];MinHeap[0]=0; //数组从第1个位置开始使用,第0个位置为0 printf("请输入小顶堆:\n"); for (int i = 1; i <= n; ++i) scanf("%d",&MinHeap[i]); int x; printf("请输入x:\n");scanf("%d",&x); MinHeapInsert(MinHeap, n, x); for (int j = 1; j <= n+1; ++j) printf("%d ", MinHeap[j]); return 0; }