c语言的数据结构 顺序表 顺序表是一种线性表,其元素在内存中连续存放,可以通过数组来实现。
顺序表是一种顺序存储的线性表,其特点如下:
数组下标与元素位置的关系:
数组下标从0开始,每个元素的索引与其在数组中的物理位置相对应。
内存地址的计算公式:
对于一个长度为n的顺序表A,其中第i个元素(假设类型为ElemType)的内存地址可以通过以下公式计算得出: [LOC(A[i]) = LOC(A[0]) + sizeof(ElemType) * i] 其中:
(LOC(A[0])) 是第一个元素(即数组下标为0的元素)的内存地址。
(sizeof(ElemType)) 表示元素类型的字节大小。
(i) 是当前元素的数组下标。
示例分析:
假设有一个整型数组A,其起始地址为1000,且每个整数占用4个字节的空间。
则第二个元素(即数组下标为1的元素)的内存地址可以计算为: [LOC(A[1]) = 1000 + 4 * 1 = 1004] 通过上述结构,我们可以方便地访问顺序表中任意位置的元素,只需知道数组的起始地址、元素类型以及目标元素的索引即可。
关于c语言的基础:
传入指针(例如 SeqList *list
)
当你传入 SeqList *list
时,表示你传递的是一个指针(也就是 list
的地址)。
传递指针的好处是你可以直接在函数内部修改指针指向的内存中的内容。
举个例子,在 InitList()
函数中,你传入了 SeqList *list
,因为你需要修改 list->length
的值,而不是修改一个副本。
1 2 3 void InitList(SeqList *list) { list->length = 0 ; }
传入结构体(例如 SeqList list
)
如果你传入的是 SeqList list
,那么传递的是整个结构体的副本。
在这种情况下,函数内对 list
的任何修改都不会影响到原始的结构体,因为函数只是对 list
进行了值传递,原来的数据并没有被改变。
例如,如果你用下面的方式传递 list
:
1 2 3 void InitList(SeqList list) { list.length = 0 ; }
3. 使用示例对比 假设你有一个顺序表,并希望使用一个函数初始化它:
1 2 SeqList list;InitList (&list);
在 InitList()
函数中,传入的是 &list
,这是 list
的地址,所以 InitList()
函数接收的是一个指向 SeqList
的指针(SeqList *list
)。
在函数内部,list
是一个指针,指向了原始的 SeqList
变量。
使用 list->length
可以直接修改原始顺序表中的 length
值。
而如果你传递 SeqList list
:
1 2 SeqList list;InitList (list);
这样 InitList()
函数接收的是 SeqList list
,它只是原始数据的副本,任何修改都不会影响原始的 list
。
4. 为什么要传递指针? 传递指针的主要原因有以下几点:
修改原始数据 :通过传递指针,你可以在函数内部修改原始数据,而不仅仅是修改副本。
提高效率 :传递一个指针比传递整个结构体更加高效,特别是当结构体较大时,传递副本会浪费大量的内存和时间。
代码的实现
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int length; } SeqList;void InitList (SeqList *list ) { list ->length = 0 ; }void PrintList (SeqList list ) { for (int i = 0 ; i < list .length; i++) { printf ("%d " , list .data[i]); } printf ("\n" ); }int Insert (SeqList *list , int pos, int value) { if (pos < 1 || pos > list ->length + 1 ) { printf ("插入位置不合法\n" ); return 0 ; } if (list ->length >= MAX_SIZE) { printf ("顺序表已满,无法插入\n" ); return 0 ; } for (int i = list ->length; i >= pos; i--) { list ->data[i] = list ->data[i - 1 ]; } list ->data[pos - 1 ] = value; list ->length++; return 1 ; }int Delete (SeqList *list , int pos) { if (pos < 1 || pos > list ->length) { printf ("删除位置不合法\n" ); return 0 ; } for (int i = pos; i < list ->length; i++) { list ->data[i - 1 ] = list ->data[i]; } list ->length--; return 1 ; }int Find (SeqList list , int value) { for (int i = 0 ; i < list .length; i++) { if (list .data[i] == value) { return i + 1 ; } } return -1 ; }int main () { SeqList list ; InitList(&list ); Insert(&list , 1 , 10 ); Insert(&list , 2 , 20 ); Insert(&list , 3 , 30 ); Insert(&list , 2 , 15 ); printf ("顺序表的内容为:" ); PrintList(list ); int pos = Find(list , 20 ); if (pos != -1 ) { printf ("元素20的位置是:%d\n" , pos); } else { printf ("未找到元素20\n" ); } Delete(&list , 3 ); printf ("删除元素后的顺序表为:" ); PrintList(list ); return 0 ; }#include <stdio.h> #include <stdlib.h> typedef struct { int *data; int size; int capacity; } SeqList; SeqList* initSeqList (int initialCapacity) { SeqList *list = (SeqList *)malloc (sizeof (SeqList)); list ->data = (int *)malloc (initialCapacity * sizeof (int )); list ->size = 0 ; list ->capacity = initialCapacity; return list ; }void expandCapacity (SeqList *list ) { int newCapacity = list ->capacity * 2 ; int *newData = (int *)realloc (list ->data, newCapacity * sizeof (int )); if (newData != NULL ) { list ->data = newData; list ->capacity = newCapacity; } else { printf ("内存分配失败!\n" ); exit (1 ); } }void insertElement (SeqList *list , int position, int value) { if (position < 0 || position > list ->size) { printf ("插入位置不合法!\n" ); return ; } if (list ->size == list ->capacity) { expandCapacity(list ); } for (int i = list ->size; i > position; i--) { list ->data[i] = list ->data[i - 1 ]; } list ->data[position] = value; list ->size++; }void deleteElement (SeqList *list , int position) { if (position < 0 || position >= list ->size) { printf ("删除位置不合法!\n" ); return ; } for (int i = position; i < list ->size - 1 ; i++) { list ->data[i] = list ->data[i + 1 ]; } list ->size--; }int findElement (SeqList *list , int value) { for (int i = 0 ; i < list ->size; i++) { if (list ->data[i] == value) { return i; } } return -1 ; }void printSeqList (SeqList *list ) { printf ("顺序表:" ); for (int i = 0 ; i < list ->size; i++) { printf ("%d " , list ->data[i]); } printf ("\n" ); }void destroySeqList (SeqList *list ) { free (list ->data); free (list ); }int main () { SeqList *list = initSeqList(5 ); insertElement(list , 0 , 10 ); insertElement(list , 1 , 20 ); insertElement(list , 2 , 30 ); insertElement(list , 3 , 40 ); insertElement(list , 4 , 50 ); printSeqList(list ); deleteElement(list , 2 ); printSeqList(list ); int pos = findElement(list , 40 ); if (pos != -1 ) { printf ("元素40的位置是:%d\n" , pos); } else { printf ("未找到元素40\n" ); } destroySeqList(list ); return 0 ; }
链表 单链表 单链表是一种链式存储的线性表结构。
结构定义:
1 2 3 4 5 typedef struct Node { int data; struct Node * next ; } LinkList;
其中:
ElemType
是节点的数据类型,可以是任何合法的数据类型。
data
域用于存储节点的实际数据。
next
域是指向下一个节点的指针。 通过这种方式,每个节点都只保存了它后面一个节点的地址,从而形成了一个单向的链接关系。
注意区分:带头节点的链表和不带头节点的链表的区别
带头节点的链表 :有一个专门的头节点,这个头节点不存储有效数据,只是作为链表的起始位置,用来简化对链表的插入、删除等操作。
不带头节点的链表 :链表的第一个节点就是存储有效数据的节点。
L->| | ->| 1 | -> | 2 |
L->| 1 | -> | 2 |
版本一:
带头节点的链表实现
使用一个额外的头节点,头节点不存储有效数据。
头节点的作用是方便对链表的操作,例如在插入、删除时,不需要判断链表是否为空。
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next ; } LinkList;void InitList (LinkList *head) { head = (LinkList *)malloc (sizeof (LinkList)); head->next = NULL ; } LinkList *AddHead () { LinkList *head, *p; int x; head = (LinkList *)malloc (sizeof (LinkList)); head->next = NULL ; printf ("请输入节点数据(输入-1结束):" ); scanf ("%d" , &x); while (x != -1 ) { p = (LinkList *)malloc (sizeof (LinkList)); p->data = x; p->next = head->next; head->next = p; scanf ("%d" , &x); } return head; } LinkList *AddTail () { LinkList *head, *p, *q; int x; head = (LinkList *)malloc (sizeof (LinkList)); head->next = NULL ; q = head; printf ("请输入节点数据(输入-1结束):" ); scanf ("%d" , &x); while (x != -1 ) { p = (LinkList *)malloc (sizeof (LinkList)); p->data = x; if (head->next == NULL ){ head->next = p; }else { q->next = p; } q = p; scanf ("%d" , &x); } if (q!=NULL ){ q->next = NULL ; } return head; }void InitList (LinkList *head) { head = (LinkList*)malloc (sizeof (LinkList)); head->next = NULL ; } Node* InitList () { Node *head = (Node*)malloc (sizeof (Node)); if (head == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } head->next = NULL ; return head; }void Insert (Node *head, int value) { Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = NULL ; Node *p = head; while (p->next != NULL ) { p = p->next; } p->next = newNode; }void InsertBefore (Node *head, Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return ; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; Node *p = head; while (p->next != NULL && p->next != target) { p = p->next; } if (p->next == target) { newNode->next = target; p->next = newNode; } else { printf ("未找到目标节点\n" ); free (newNode); } }void InsertAfter (Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return ; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = target->next; target->next = newNode; }void PrintList (Node *head) { Node *p = head->next; while (p != NULL ) { printf ("%d -> " , p->data); p = p->next; } printf ("NULL\n" ); }void Delete (Node *head, int value) { Node *p = head; while (p->next != NULL && p->next->data != value) { p = p->next; } if (p->next != NULL ) { Node *temp = p->next; p->next = temp->next; free (temp); } }void DeleteAt (Node *head, int index) { if (index < 0 ) { printf ("无效的索引\n" ); return ; } Node *p = head; int currentIndex = 0 ; while (p->next != NULL && currentIndex < index) { p = p->next; currentIndex++; } if (p->next != NULL ) { Node *temp = p->next; p->next = temp->next; free (temp); } else { printf ("索引超出范围\n" ); } } Node* FindByValue (Node *head, int value) { Node *p = head->next; while (p != NULL ) { if (p->data == value) { return p; } p = p->next; } return NULL ; } Node* FindByIndex (Node *head, int index) { if (index < 0 ) { return NULL ; } Node *p = head->next; int currentIndex = 0 ; while (p != NULL && currentIndex < index) { p = p->next; currentIndex++; } return p; }int GetLength (Node *head) { int length = 0 ; Node *p = head->next; while (p != NULL ) { length++; p = p->next; } return length; }int main () { Node *head = InitList(); Insert(head, 10 ); Insert(head, 20 ); Insert(head, 30 ); Insert(head, 40 ); printf ("链表内容为:" ); PrintList(head); InsertBefore(head, head->next->next, 25 ); printf ("在值为30的节点之前插入值为25的节点后链表内容为:" ); PrintList(head); InsertAfter(head->next->next, 27 ); printf ("在值为25的节点之后插入值为27的节点后链表内容为:" ); PrintList(head); Delete(head, 20 ); printf ("删除值为20的节点后链表内容为:" ); PrintList(head); DeleteAt(head, 2 ); printf ("删除索引为2的节点后链表内容为:" ); PrintList(head); Node *foundNode = FindByValue(head, 30 ); if (foundNode != NULL ) { printf ("找到值为30的节点,地址为:%p\n" , foundNode); } else { printf ("未找到值为30的节点\n" ); } foundNode = FindByIndex(head, 1 ); if (foundNode != NULL ) { printf ("找到索引为1的节点,值为:%d\n" , foundNode->data); } else { printf ("未找到索引为1的节点\n" ); } int length = GetLength(head); printf ("链表长度为:%d\n" , length); Node *p = head; while (p != NULL ) { Node *temp = p; p = p->next; free (temp); }
不带头节点的链表实现
没有额外的头节点,第一个节点直接存储有效数据。
在对链表进行插入、删除操作时,需要考虑链表为空或者操作的节点为第一个节点的特殊情况。
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next ; } LinkList; Node* InitList () { return NULL ; } LinkList *AddHead () { LinkList *head = NULL ; LinkList *p; int x; printf ("请输入节点数据(输入-1结束):" ); scanf ("%d" , &x); while (x != -1 ) { p = (LinkList *)malloc (sizeof (LinkList)); p->data = x; p->next = head; head = p; scanf ("%d" , &x); } return head; } LinkList *AddTail () { LinkList *head = NULL ; LinkList *p, *q = NULL ; int x; printf ("请输入节点数据(输入-1结束):" ); scanf ("%d" , &x); while (x != -1 ) { p = (LinkList *)malloc (sizeof (LinkList)); p->data = x; p->next = NULL ; if (head == NULL ) { head = p; } else { q->next = p; } q = p; scanf ("%d" , &x); } return head; } Node* Insert (Node *head, int value) { Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = NULL ; if (head == NULL ) { return newNode; } Node *p = head; while (p->next != NULL ) { p = p->next; } p->next = newNode; return head; } Node* InsertBefore (Node *head, Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return head; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; if (head == target) { newNode->next = head; return newNode; } Node *p = head; while (p != NULL && p->next != target) { p = p->next; } if (p != NULL ) { newNode->next = target; p->next = newNode; } else { printf ("未找到目标节点\n" ); free (newNode); } return head; }void InsertAfter (Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return ; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = target->next; target->next = newNode; }void PrintList (Node *head) { Node *p = head; while (p != NULL ) { printf ("%d -> " , p->data); p = p->next; } printf ("NULL\n" ); } Node* Delete (Node *head, int value) { if (head == NULL ) { return head; } if (head->data == value) { Node *temp = head; head = head->next; free (temp); return head; } Node *p = head; while (p->next != NULL && p->next->data != value) { p = p->next; } if (p->next != NULL ) { Node *temp = p->next; p->next = temp->next; free (temp); } return head; } Node* DeleteAt (Node *head, int index) { if (index < 0 ) { printf ("无效的索引\n" ); return head; } if (index == 0 && head != NULL ) { Node *temp = head; head = head->next; free (temp); return head; } Node *p = head; int currentIndex = 0 ; while (p != NULL && p->next != NULL && currentIndex < index - 1 ) { p = p->next; currentIndex++; } if (p != NULL && p->next != NULL ) { Node *temp = p->next; p->next = temp->next; free (temp); } else { printf ("索引超出范围\n" ); } return head; } Node* FindByValue (Node *head, int value) { Node *p = head; while (p != NULL ) { if (p->data == value) { return p; } p = p->next; } return NULL ; } Node* FindByIndex (Node *head, int index) { if (index < 0 ) { return NULL ; } Node *p = head; int currentIndex = 0 ; while (p != NULL && currentIndex < index) { p = p->next; currentIndex++; } return p; }int GetLength (Node *head) { int length = 0 ; Node *p = head; while (p != NULL ) { length++; p = p->next; } return length; }int main () { Node *head = InitList(); head = Insert(head, 10 ); head = Insert(head, 20 ); head = Insert(head, 30 ); head = Insert(head, 40 ); printf ("链表内容为:" ); PrintList(head); head = InsertBefore(head, head->next->next, 25 ); printf ("在值为30的节点之前插入值为25的节点后链表内容为:" ); PrintList(head); InsertAfter(head->next->next, 27 ); printf ("在值为25的节点之后插入值为27的节点后链表内容为:" ); PrintList(head); head = Delete(head, 20 ); printf ("删除值为20的节点后链表内容为:" ); PrintList(head); head = DeleteAt(head, 2 ); printf ("删除索引为2的节点后链表内容为:" ); PrintList(head); Node *foundNode = FindByValue(head, 30 ); if (foundNode != NULL ) { printf ("找到值为30的节点,地址为:%p\n" , foundNode); } else { printf ("未找到值为30的节点\n" ); } foundNode = FindByIndex(head, 1 ); if (foundNode != NULL ) { printf ("找到索引为1的节点,值为:%d\n" , foundNode->data); } else { printf ("未找到索引为1的节点\n" ); } int length = GetLength(head); printf ("链表长度为:%d\n" , length); Node *p = head; while (p != NULL ) { Node *temp = p; p = p->next; free (temp); } return 0 ; }
带头节点的链表
初始化时创建一个头节点,头节点不存储有效数据。
插入、删除、遍历等操作都从头节点开始,简化了对第一个节点的特殊判断。
例如在Insert()
和Delete()
函数中操作时,由于有头节点的存在,所有的节点都可以用统一的方式来处理。
不带头节点的链表
初始化时直接设置为空链表(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 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *next; struct Node *prev; } Node;Node* InitList () { return NULL ; }Node* Insert (Node *head, int value) { Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = NULL ; newNode->prev = NULL ; if (head == NULL ) { return newNode; } Node *p = head; while (p->next != NULL ) { p = p->next; } p->next = newNode; newNode->prev = p; return head; }Node* InsertBefore (Node *head, Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return head; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; if (head == target) { newNode->next = head; newNode->prev = NULL ; head->prev = newNode; return newNode; } Node *p = target->prev; newNode->next = target; newNode->prev = p; if (p != NULL ) { p->next = newNode; } target->prev = newNode; return head; }void InsertAfter (Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return ; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = target->next; newNode->prev = target; if (target->next != NULL ) { target->next->prev = newNode; } target->next = newNode; }void PrintList (Node *head) { Node *p = head; while (p != NULL ) { printf ("%d -> " , p->data); p = p->next; } printf ("NULL\n" ); }Node* Delete (Node *head, int value) { Node *p = head; while (p != NULL && p->data != value) { p = p->next; } if (p == NULL ) { return head; } if (p->prev != NULL ) { p->prev->next = p->next; } else { head = p->next; } if (p->next != NULL ) { p->next->prev = p->prev; } free (p); return head; }Node* DeleteAt (Node *head, int index) { if (index < 0 ) { printf ("无效的索引\n" ); return head; } Node *p = head; int currentIndex = 0 ; while (p != NULL && currentIndex < index) { p = p->next; currentIndex++; } if (p == NULL ) { printf ("索引超出范围\n" ); return head; } if (p->prev != NULL ) { p->prev->next = p->next; } else { head = p->next; } if (p->next != NULL ) { p->next->prev = p->prev; } free (p); return head; }Node* FindByValue (Node *head, int value) { Node *p = head; while (p != NULL ) { if (p->data == value) { return p; } p = p->next; } return NULL ; }Node* FindByIndex (Node *head, int index) { if (index < 0 ) { return NULL ; } Node *p = head; int currentIndex = 0 ; while (p != NULL && currentIndex < index) { p = p->next; currentIndex++; } return p; }int GetLength (Node *head) { int length = 0 ; Node *p = head; while (p != NULL ) { length++; p = p->next; } return length; }int main () { Node *head = InitList (); head = Insert (head, 10 ); head = Insert (head, 20 ); head = Insert (head, 30 ); head = Insert (head, 40 ); printf ("链表内容为:" ); PrintList (head); head = InsertBefore (head, head->next->next, 25 ); printf ("在值为30的节点之前插入值为25的节点后链表内容为:" ); PrintList (head); InsertAfter (head->next->next, 27 ); printf ("在值为25的节点之后插入值为27的节点后链表内容为:" ); PrintList (head); head = Delete (head, 20 ); printf ("删除值为20的节点后链表内容为:" ); PrintList (head); head = DeleteAt (head, 2 ); printf ("删除索引为2的节点后链表内容为:" ); PrintList (head); Node *foundNode = FindByValue (head, 30 ); if (foundNode != NULL ) { printf ("找到值为30的节点,地址为:%p\n" , foundNode); } else { printf ("未找到值为30的节点\n" ); } foundNode = FindByIndex (head, 1 ); if (foundNode != NULL ) { printf ("找到索引为1的节点,值为:%d\n" , foundNode->data); } else { printf ("未找到索引为1的节点\n" ); } int length = GetLength (head); printf ("链表长度为:%d\n" , length); Node *p = head; while (p != NULL ) { Node *temp = p; p = p->next; free (temp); } return 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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *next; struct Node *prev; } Node;Node* InitList () { return NULL ; }Node* Insert (Node *head, int value) { Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; if (head == NULL ) { newNode->next = newNode; newNode->prev = newNode; return newNode; } Node *tail = head->prev; tail->next = newNode; newNode->prev = tail; newNode->next = head; head->prev = newNode; return head; }Node* InsertBefore (Node *head, Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return head; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; Node *prevNode = target->prev; newNode->next = target; newNode->prev = prevNode; target->prev = newNode; prevNode->next = newNode; if (target == head) { return newNode; } return head; }void InsertAfter (Node *target, int value) { if (target == NULL ) { printf ("目标节点为空,无法插入\n" ); return ; } Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; Node *nextNode = target->next; newNode->next = nextNode; newNode->prev = target; target->next = newNode; nextNode->prev = newNode; }void PrintList (Node *head) { if (head == NULL ) { printf ("NULL\n" ); return ; } Node *p = head; do { printf ("%d -> " , p->data); p = p->next; } while (p != head); printf ("(回到头节点)\n" ); }Node* Delete (Node *head, int value) { if (head == NULL ) { return head; } Node *p = head; do { if (p->data == value) { if (p->next == p) { free (p); return NULL ; } Node *prevNode = p->prev; Node *nextNode = p->next; prevNode->next = nextNode; nextNode->prev = prevNode; if (p == head) { head = nextNode; } free (p); return head; } p = p->next; } while (p != head); return head; }Node* DeleteAt (Node *head, int index) { if (head == NULL || index < 0 ) { printf ("无效的索引\n" ); return head; } Node *p = head; int currentIndex = 0 ; do { if (currentIndex == index) { if (p->next == p) { free (p); return NULL ; } Node *prevNode = p->prev; Node *nextNode = p->next; prevNode->next = nextNode; nextNode->prev = prevNode; if (p == head) { head = nextNode; } free (p); return head; } p = p->next; currentIndex++; } while (p != head); printf ("索引超出范围\n" ); return head; }Node* FindByValue (Node *head, int value) { if (head == NULL ) { return NULL ; } Node *p = head; do { if (p->data == value) { return p; } p = p->next; } while (p != head); return NULL ; }Node* FindByIndex (Node *head, int index) { if (head == NULL || index < 0 ) { return NULL ; } Node *p = head; int currentIndex = 0 ; do { if (currentIndex == index) { return p; } p = p->next; currentIndex++; } while (p != head); return NULL ; }int GetLength (Node *head) { if (head == NULL ) { return 0 ; } int length = 0 ; Node *p = head; do { length++; p = p->next; } while (p != head); return length; }int main () { Node *head = InitList (); head = Insert (head, 10 ); head = Insert (head, 20 ); head = Insert (head, 30 ); head = Insert (head, 40 ); printf ("链表内容为:" ); PrintList (head); head = InsertBefore (head, head->next->next, 25 ); printf ("在值为30的节点之前插入值为25的节点后链表内容为:" ); PrintList (head); InsertAfter (head->next->next, 27 ); printf ("在值为25的节点之后插入值为27的节点后链表内容为:" ); PrintList (head); head = Delete (head, 20 ); printf ("删除值为20的节点后链表内容为:" ); PrintList (head); head = DeleteAt (head, 2 ); printf ("删除索引为2的节点后链表内容为:" ); PrintList (head); Node *foundNode = FindByValue (head, 30 ); if (foundNode != NULL ) { printf ("找到值为30的节点,地址为:%p\n" , foundNode); } else { printf ("未找到值为30的节点\n" ); } foundNode = FindByIndex (head, 1 ); if (foundNode != NULL ) { printf ("找到索引为1的节点,值为:%d\n" , foundNode->data); } else { printf ("未找到索引为1的节点\n" ); } int length = GetLength (head); printf ("链表长度为:%d\n" , length); if (head != NULL ) { Node *p = head; do { Node *temp = p; p = p->next; free (temp); } while (p != head); } return 0 ; }
题目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next; } Node;void reverseList (Node **head) { Node *prev = NULL ; Node *curr = *head; Node *next = NULL ; while (curr != NULL ) { next = curr->next; curr->next = prev; prev = curr; curr = next; } *head = prev; }
对给定的单链表L,编写一个删除中值为×的结点的直接前驱结点的算法
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 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next; } Node;Node* createNode (int data) { Node *newNode = (Node *)malloc (sizeof (Node)); newNode->data = data; newNode->next = NULL ; return newNode; }void deletePredecessor (Node *head, int x) { if (head == NULL || head->next == NULL ) { printf ("链表为空或只有一个节点,无法删除前驱节点!\n" ); return ; } Node *prev = NULL ; Node *curr = head; while (curr != NULL && curr->next != NULL ) { if (curr->next->data == x) { Node *temp = prev; if (temp != NULL ) { temp->next = curr->next; free (curr); } return ; } prev = curr; curr = curr->next; } printf ("未找到值为 %d 的节点或该节点没有前驱节点!\n" , x); }
有两个循环单链表,链表头指针分别为 headl 和 head2, -个函数将链表 headl 链接到链表head2 之后,链接后的链表仍保持是循环链表的形式。
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 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next; } LinkList;LinkList* InitCircularList () { LinkList *head = (LinkList*)malloc (sizeof (LinkList)); head->next = head; return head; }void AddHead (LinkList *head, int data) { LinkList *newNode = (LinkList*)malloc (sizeof (LinkList)); newNode->data = data; newNode->next = head->next; head->next = newNode; }void AddTail (LinkList *head, int data) { LinkList *newNode = (LinkList*)malloc (sizeof (LinkList)); newNode->data = data; newNode->next = head; LinkList *p = head; while (p->next != head) { p = p->next; } p->next = newNode; }void PrintList (LinkList *head) { LinkList *p = head->next; while (p != head) { printf ("%d -> " , p->data); p = p->next; } printf ("回到头结点\n" ); }void ConnectCircularLists (LinkList *head1, LinkList *head2) { if (head1 == NULL || head2 == NULL ) { return ; } LinkList *p1 = head1; LinkList *p2 = head2; while (p1->next != head1) { p1 = p1->next; } while (p2->next != head2) { p2 = p2->next; } p1->next = head2->next; p2->next = head1; }int main () { LinkList *head1 = InitCircularList (); LinkList *head2 = InitCircularList (); AddTail (head1, 1 ); AddTail (head1, 2 ); AddTail (head1, 3 ); printf ("链表 head1:\n" ); PrintList (head1); AddTail (head2, 4 ); AddTail (head2, 5 ); AddTail (head2, 6 ); printf ("链表 head2:\n" ); PrintList (head2); ConnectCircularLists (head1, head2); printf ("合并后的循环链表:\n" ); PrintList (head1); return 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 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node * next; } LinkList;LinkList* InitList () { LinkList* head = (LinkList*)malloc (sizeof (LinkList)); head->next = NULL ; return head; }void AddTail (LinkList* head, int data) { LinkList* newNode = (LinkList*)malloc (sizeof (LinkList)); newNode->data = data; newNode->next = NULL ; LinkList* p = head; while (p->next != NULL ) { p = p->next; } p->next = newNode; }LinkList* CopyList (LinkList* head) { if (head == NULL ) return NULL ; LinkList* newHead = (LinkList*)malloc (sizeof (LinkList)); newHead->next = NULL ; LinkList* p = head->next; LinkList* q = newHead; while (p != NULL ) { LinkList* newNode = (LinkList*)malloc (sizeof (LinkList)); newNode->data = p->data; newNode->next = NULL ; q->next = newNode; q = newNode; p = p->next; } return newHead; }void PrintList (LinkList* head) { LinkList* p = head->next; while (p != NULL ) { printf ("%d -> " , p->data); p = p->next; } printf ("NULL\n" ); }int main () { LinkList* head = InitList (); AddTail (head, 1 ); AddTail (head, 2 ); AddTail (head, 3 ); printf ("原链表: " ); PrintList (head); LinkList* copiedHead = CopyList (head); printf ("复制后的链表: " ); PrintList (copiedHead); return 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 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int top; } Stack;void InitStack (Stack *stack) { stack->top = -1 ; }int IsEmpty (Stack *stack) { return stack->top == -1 ; }int IsFull (Stack *stack) { return stack->top == MAX_SIZE - 1 ; }int Push (Stack *stack, int value) { if (IsFull (stack)) { printf ("栈满,无法入栈\n" ); return 0 ; } stack->data[++stack->top] = value; return 1 ; }int Pop (Stack *stack) { if (IsEmpty (stack)) { printf ("栈空,无法出栈\n" ); return -1 ; } return stack->data[stack->top--]; }int Peek (Stack *stack) { if (IsEmpty (stack)) { printf ("栈空,无法查看栈顶元素\n" ); return -1 ; } return stack->data[stack->top]; }int main () { Stack stack; InitStack (&stack); Push (&stack, 10 ); Push (&stack, 20 ); Push (&stack, 30 ); int value = Peek (&stack); if (value != -1 ) { printf ("栈顶元素为: %d\n" , value); } while (!IsEmpty (&stack)) { value = Pop (&stack); if (value != -1 ) { printf ("出栈元素: %d\n" , value); } } return 0 ; }
栈的链式结构 top -> 顶 -> … -> 底
栈顶结点:
位于链表的头部,指向下一个元素。栈底结点:
位于链表的尾部,没有后续元素。链表表示: 1 top -> a1 -> a2 -> ... -> an
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 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int top; } Stack;void InitStack (Stack *stack) { stack->top = -1 ; }int IsEmpty (Stack *stack) { return stack->top == -1 ; }int IsFull (Stack *stack) { return stack->top == MAX_SIZE - 1 ; }int Push (Stack *stack, int value) { if (IsFull (stack)) { printf ("栈满,无法入栈\n" ); return 0 ; } stack->data[++stack->top] = value; return 1 ; }int Pop (Stack *stack) { if (IsEmpty (stack)) { printf ("栈空,无法出栈\n" ); return -1 ; } return stack->data[stack->top--]; }int Peek (Stack *stack) { if (IsEmpty (stack)) { printf ("栈空,无法查看栈顶元素\n" ); return -1 ; } return stack->data[stack->top]; }int main () { Stack stack; InitStack (&stack); Push (&stack, 10 ); Push (&stack, 20 ); Push (&stack, 30 ); int value = Peek (&stack); if (value != -1 ) { printf ("栈顶元素为: %d\n" , value); } while (!IsEmpty (&stack)) { value = Pop (&stack); if (value != -1 ) { printf ("出栈元素: %d\n" , value); } } return 0 ; }
队列 队列是一种先进先出(FIFO)的数据结构,元素从队尾入队,从队头出队。它的基本操作包括入队(Enqueue)、出队(Dequeue)、取队头(Peek/Front)和判定队空(IsEmpty)。
队列(Queue) :一种操作受限的线性表,只允许在表的一端进行插入,而在表的另一端进行删除。
基本操作:
入队或进队(Enqueue) :元素从队列尾部进入队列。
出队或离队(Dequeue) :元素从队列头部离开队列。特性:
先进先出(First-In-First-Out, FIFO) 结构示意图: 1 2 3 a1 , a2 , a3 , a4 ↑ ↑ 队头(队首) 队尾
允许插入的一端称为队尾 ,
允许删除的一端称为队首 ,
队列具有先进先出 的特点。 通过以上笔记,可以清晰地理解队列的定义、操作以及其特有的“先进先出”的特性。
好的,以下是关于队列存储结构的数据结构笔记:
队列的顺序存储 定义:
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 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct Queue { int data[MAX_SIZE]; int front; int rear; } Queue;Queue* InitQueue () { Queue *queue = (Queue*)malloc (sizeof (Queue)); if (queue == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } queue->front = 0 ; queue->rear = 0 ; return queue; }int IsEmpty (Queue *queue) { return queue->front == queue->rear; }int IsFull (Queue *queue) { return queue->rear == MAX_SIZE; }void Enqueue (Queue *queue, int value) { if (IsFull (queue)) { printf ("队列已满,无法插入元素\n" ); return ; } queue->data[queue->rear] = value; queue->rear++; }int Dequeue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法删除元素\n" ); exit (1 ); } int value = queue->data[queue->front]; queue->front++; return value; }int GetFront (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法获取元素\n" ); exit (1 ); } return queue->data[queue->front]; }void PrintQueue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空\n" ); return ; } for (int i = queue->front; i < queue->rear; i++) { printf ("%d -> " , queue->data[i]); } printf ("NULL\n" ); }int main () { Queue *queue = InitQueue (); Enqueue (queue, 10 ); Enqueue (queue, 20 ); Enqueue (queue, 30 ); Enqueue (queue, 40 ); printf ("队列内容为:" ); PrintQueue (queue); int dequeuedValue = Dequeue (queue); printf ("出队的元素为:%d\n" , dequeuedValue); printf ("出队后队列内容为:" ); PrintQueue (queue); int frontValue = GetFront (queue); printf ("队列头部元素为:%d\n" , frontValue); free (queue); return 0 ; }
关键点:
定义结构体 Queue :
int data[MAX_SIZE];
用数组存储队列元素,MAX_SIZE
为队列的最大容量。
int front;
表示队列头部的索引。
int rear;
表示队列尾部的索引。
初始化队列 :
InitQueue()
初始化队列,front
和 rear
都设为 0,表示队列为空。
判断队列状态 :
IsEmpty(queue)
判断队列是否为空:front == rear
表示队列为空。
IsFull(queue)
判断队列是否已满:rear == MAX_SIZE
表示队列满。
入队操作(Enqueue) :
Enqueue(queue, value)
向队列末尾插入新元素,将新值存储在 data[rear]
,然后 rear++
。
出队操作(Dequeue) :
Dequeue(queue)
删除队列头部元素,从队列头部移除元素,返回 data[front]
的值,然后 front++
。
获取队列头部元素 :
GetFront(queue)
返回队列头部的元素,不改变队列结构。
打印队列内容 :
PrintQueue(queue)
从 front
到 rear
打印队列中的所有元素。
队列的链式存储 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 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *next; } Node;typedef struct Queue { Node *front; Node *rear; } Queue;Queue* InitQueue () { Queue *queue = (Queue*)malloc (sizeof (Queue)); if (queue == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } queue->front = NULL ; queue->rear = NULL ; return queue; }int IsEmpty (Queue *queue) { return queue->front == NULL ; }void Enqueue (Queue *queue, int value) { Node *newNode = (Node*)malloc (sizeof (Node)); if (newNode == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = value; newNode->next = NULL ; if (IsEmpty (queue)) { queue->front = newNode; queue->rear = newNode; } else { queue->rear->next = newNode; queue->rear = newNode; } }int Dequeue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法删除元素\n" ); exit (1 ); } Node *temp = queue->front; int value = temp->data; queue->front = queue->front->next; if (queue->front == NULL ) { queue->rear = NULL ; } free (temp); return value; }int GetFront (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法获取元素\n" ); exit (1 ); } return queue->front->data; }void PrintQueue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空\n" ); return ; } Node *p = queue->front; while (p != NULL ) { printf ("%d -> " , p->data); p = p->next; } printf ("NULL\n" ); }int main () { Queue *queue = InitQueue (); Enqueue (queue, 10 ); Enqueue (queue, 20 ); Enqueue (queue, 30 ); Enqueue (queue, 40 ); printf ("队列内容为:" ); PrintQueue (queue); int dequeuedValue = Dequeue (queue); printf ("出队的元素为:%d\n" , dequeuedValue); printf ("出队后队列内容为:" ); PrintQueue (queue); int frontValue = GetFront (queue); printf ("队列头部元素为:%d\n" , frontValue); while (!IsEmpty (queue)) { Dequeue (queue); } free (queue); return 0 ;
循环队列 循环队列是队列的一种变形,它使用数组实现,并通过将队尾指针和队头指针进行“环绕”处理,以有效利用数组空间。以下是循环队列的进队和出队操作。把存储队列元素的表从逻辑上视为一个环。
rear
指针总是指向一个空位置,这就是为什么循环队列总会有一个空位置的原因。这个设计目的是为了区分 队列为空和 队列已满**的状态。
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 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct Queue { int data[MAX_SIZE]; int front; int rear; } Queue;Queue* InitQueue () { Queue *queue = (Queue*)malloc (sizeof (Queue)); if (queue == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } queue->front = 0 ; queue->rear = 0 ; return queue; }int IsEmpty (Queue *queue) { return queue->front == queue->rear; }int IsFull (Queue *queue) { return (queue->rear + 1 ) % MAX_SIZE == queue->front; }void Enqueue (Queue *queue, int value) { if (IsFull (queue)) { printf ("队列已满,无法插入元素\n" ); return ; } queue->data[queue->rear] = value; queue->rear = (queue->rear + 1 ) % MAX_SIZE; }int Dequeue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法删除元素\n" ); exit (1 ); } int value = queue->data[queue->front]; queue->front = (queue->front + 1 ) % MAX_SIZE; return value; }int GetFront (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空,无法获取元素\n" ); exit (1 ); } return queue->data[queue->front]; }void PrintQueue (Queue *queue) { if (IsEmpty (queue)) { printf ("队列为空\n" ); return ; } int i = queue->front; while (i != queue->rear) { printf ("%d -> " , queue->data[i]); i = (i + 1 ) % MAX_SIZE; } printf ("NULL\n" ); }int main () { Queue *queue = InitQueue (); Enqueue (queue, 10 ); Enqueue (queue, 20 ); Enqueue (queue, 30 ); Enqueue (queue, 40 ); printf ("队列内容为:" ); PrintQueue (queue); int dequeuedValue = Dequeue (queue); printf ("出队的元素为:%d\n" , dequeuedValue); printf ("出队后队列内容为:" ); PrintQueue (queue); int frontValue = GetFront (queue); printf ("队列头部元素为:%d\n" , frontValue); free (queue); return 0 ; }
循环队列中的关键公式与要点总结
初始状态
[ Q_front = Q_rear = 0 ]
队首指针 Q_front
和队尾指针 Q_rear
都初始化为 0
,表示队列为空。
队首指针进 1(出队操作)
[ Q_front = (Q_front + 1) % MaxSize ]
当有元素出队时,队首指针 Q_front
向前移动一位,使用取模操作 % MaxSize
确保它可以循环到数组的开头。
队尾指针进 1(入队操作)
[ Q_rear = (Q_rear + 1) % MaxSize ]
当有新元素入队时,队尾指针 Q_rear
向前移动一位,取模操作 % MaxSize
确保它可以循环到数组的开头。
队列长度计算公式
[ \text{元素个数} = (Q_rear + MaxSize - Q_front) % MaxSize ]
该公式用于计算当前队列中的元素个数。
(Q_rear + MaxSize - Q_front)
用于计算 rear
和 front
之间的距离,无论它们的位置关系如何。
取模操作 % MaxSize
确保结果在合法范围内。
队列状态判断
队列为空 :当 Q_front == Q_rear
时,表示队列为空。
队列已满 :当 (Q_rear + 1) % MaxSize == Q_front
时,表示队列已满(保留了一个空位置用于区分空和满的状态)。
题目 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 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_SIZE 100 typedef struct { char data[MAX_SIZE]; int top; } Stack;void InitStack (Stack *stack) { stack->top = -1 ; }int IsEmpty (Stack *stack) { return stack->top == -1 ; }int IsFull (Stack *stack) { return stack->top == MAX_SIZE - 1 ; }int Push (Stack *stack, char value) { if (IsFull (stack)) { return 0 ; } stack->data[++stack->top] = value; return 1 ; }char Pop (Stack *stack) { if (IsEmpty (stack)) { return '\0' ; } return stack->data[stack->top--]; }char Peek (Stack *stack) { if (IsEmpty (stack)) { return '\0' ; } return stack->data[stack->top]; }int IsMatchingPair (char left, char right) { return (left == '(' && right == ')' ) || (left == '{' && right == '}' ) || (left == '[' && right == ']' ); }int AreParenthesesBalanced (const char *expr) { Stack stack; InitStack (&stack); for (int i = 0 ; i < strlen (expr); i++) { char ch = expr[i]; if (ch == '(' || ch == '{' || ch == '[' ) { Push (&stack, ch); } else if (ch == ')' || ch == '}' || ch == ']' ) { if (IsEmpty (&stack) || !IsMatchingPair (Pop (&stack), ch)) { return 0 ; } } } return IsEmpty (&stack); }int main () { const char *expr = "{[()]}" ; if (AreParenthesesBalanced (expr)) { printf ("括号匹配成功\n" ); } else { printf ("括号不匹配\n" ); } return 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 #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int front; int rear; } Queue;void InitQueue (Queue *q) { q->front = 0 ; q->rear = 0 ; }int IsEmpty (Queue *q) { return q->front == q->rear; }int IsFull (Queue *q) { return (q->rear + 1 ) % MAX_SIZE == q->front; }int Enqueue (Queue *q, int value) { if (IsFull (q)) { return 0 ; } q->data[q->rear] = value; q->rear = (q->rear + 1 ) % MAX_SIZE; return 1 ; }int Dequeue (Queue *q, int *value) { if (IsEmpty (q)) { return 0 ; } *value = q->data[q->front]; q->front = (q->front + 1 ) % MAX_SIZE; return 1 ; }void PrintYangHuiTriangle (int rows) { Queue q; InitQueue (&q); Enqueue (&q, 1 ); for (int i = 1 ; i <= rows; i++) { int prev = 0 , current; for (int j = 0 ; j < i; j++) { Dequeue (&q, ¤t); printf ("%d " , current); Enqueue (&q, prev + current); prev = current; } Enqueue (&q, 1 ); printf ("\n" ); } }int main () { int rows; printf ("请输入杨辉三角的行数: " ); scanf ("%d" , &rows); PrintYangHuiTriangle (rows); return 0 ; }
设有两个栈 sl、s2都采用顺序栈方式,并且共享一个存储区[0.MAXSIZE-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 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 #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 typedef struct { int data[MAXSIZE]; int top1; int top2; } SharedStack;void InitStack (SharedStack *stack) { stack->top1 = -1 ; stack->top2 = MAXSIZE; }int Push (SharedStack *stack, int value, int flag) { if (stack->top1 + 1 == stack->top2) { printf ("栈满,无法入栈\n" ); return 0 ; } if (flag == 1 ) { stack->data[++stack->top1] = value; } else if (flag == 2 ) { stack->data[--stack->top2] = value; } else { printf ("无效的栈标识\n" ); return 0 ; } return 1 ; }int Pop (SharedStack *stack, int *value, int flag) { if (flag == 1 ) { if (stack->top1 == -1 ) { printf ("栈s1空,无法出栈\n" ); return 0 ; } *value = stack->data[stack->top1--]; } else if (flag == 2 ) { if (stack->top2 == MAXSIZE) { printf ("栈s2空,无法出栈\n" ); return 0 ; } *value = stack->data[stack->top2++]; } else { printf ("无效的栈标识\n" ); return 0 ; } return 1 ; }int main () { SharedStack stack; InitStack (&stack); Push (&stack, 10 , 1 ); Push (&stack, 20 , 1 ); Push (&stack, 30 , 1 ); Push (&stack, 100 , 2 ); Push (&stack, 200 , 2 ); Push (&stack, 300 , 2 ); int value; printf ("栈s1出栈元素: " ); while (Pop (&stack, &value, 1 )) { printf ("%d " , value); } printf ("\n" ); printf ("栈s2出栈元素: " ); while (Pop (&stack, &value, 2 )) { printf ("%d " , value); } printf ("\n" ); return 0 ; }
假设用一个循环单链表来表示循环队列,该队列只设一个队尾指针,不设队头指针编写如下函数: (1)向循环链队中插入一个元素为x的结点。 (2)从循环链队中删除一个结点。
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 #include <stdio.h> #include <stdlib.h> typedef struct Node { int data; struct Node *next; } Node;typedef struct { Node *rear; } CircularQueue;void InitQueue (CircularQueue *queue) { queue->rear = NULL ; }int QueueOperation (CircularQueue *queue, int *value, int isEnqueue) { if (isEnqueue) { Node *newNode = (Node *)malloc (sizeof (Node)); if (!newNode) { printf ("内存分配失败\n" ); exit (1 ); } newNode->data = *value; if (!queue->rear) { newNode->next = newNode; queue->rear = newNode; } else { newNode->next = queue->rear->next; queue->rear->next = newNode; queue->rear = newNode; } return 1 ; } else { if (!queue->rear) { printf ("队列为空,无法出队\n" ); return 0 ; } Node *temp = queue->rear->next; *value = temp->data; if (queue->rear == temp) { queue->rear = NULL ; } else { queue->rear->next = temp->next; } free (temp); return 1 ; } }int main () { CircularQueue queue; InitQueue (&queue); int value = 10 ; QueueOperation (&queue, &value, 1 ); value = 20 ; QueueOperation (&queue, &value, 1 ); value = 30 ; QueueOperation (&queue, &value, 1 ); if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (QueueOperation (&queue, &value, 0 )) { printf ("队列为空,无法出队\n" ); } return 0 ; }
假设将循环队列定义为:以域变量rear 和 length 分别表示循环队列中队尾元素的位和内含元素的个数。试给出循环队列的队满条件,并写出相应的入队和出队的算法。
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 #include <stdio.h> #include <stdlib.h> #define MAXSIZE 100 typedef struct { int data[MAXSIZE]; int rear; int length; } CircularQueue;void InitQueue (CircularQueue *queue) { queue->rear = -1 ; queue->length = 0 ; }int QueueOperation (CircularQueue *queue, int *value, int isEnqueue) { if (isEnqueue) { if (queue->length == MAXSIZE) { printf ("队列已满,无法入队\n" ); return 0 ; } queue->rear = (queue->rear + 1 ) % MAXSIZE; queue->data[queue->rear] = *value; queue->length++; return 1 ; } else { if (queue->length == 0 ) { printf ("队列为空,无法出队\n" ); return 0 ; } int front = (queue->rear - queue->length + 1 + MAXSIZE) % MAXSIZE; *value = queue->data[front]; queue->length--; return 1 ; } }int main () { CircularQueue queue; InitQueue (&queue); int value = 10 ; QueueOperation (&queue, &value, 1 ); value = 20 ; QueueOperation (&queue, &value, 1 ); value = 30 ; QueueOperation (&queue, &value, 1 ); if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (QueueOperation (&queue, &value, 0 )) { printf ("出队元素: %d\n" , value); } if (!QueueOperation (&queue, &value, 0 )) { printf ("队列为空,无法出队\n" ); } return 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 #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXSIZE 100 typedef struct { char data[MAXSIZE]; int length; } SString;void InitString (SString *str) { str->length = 0 ; str->data[0 ] = '\0' ; }int SubString (SString *str, SString *substr, int pos, int len) { if (pos < 0 || pos >= str->length || len < 0 || pos + len > str->length) { printf ("位置或长度不合法\n" ); return 0 ; } for (int i = 0 ; i < len; i++) { substr->data[i] = str->data[pos + i]; } substr->data[len] = '\0' ; substr->length = len; return 1 ; }int InsertString (SString *str, int pos, SString *substr) { if (pos < 0 || pos > str->length || str->length + substr->length > MAXSIZE) { printf ("插入位置或长度不合法\n" ); return 0 ; } for (int i = str->length - 1 ; i >= pos; i--) { str->data[i + substr->length] = str->data[i]; } for (int i = 0 ; i < substr->length; i++) { str->data[pos + i] = substr->data[i]; } str->length += substr->length; str->data[str->length] = '\0' ; return 1 ; }int DeleteString (SString *str, int pos, int len) { if (pos < 0 || pos >= str->length || len < 0 || pos + len > str->length) { printf ("删除位置或长度不合法\n" ); return 0 ; } for (int i = pos + len; i < str->length; i++) { str->data[i - len] = str->data[i]; } str->length -= len; str->data[str->length] = '\0' ; return 1 ; }int ConcatString (SString *result, SString *str1, SString *str2) { if (str1->length + str2->length > MAXSIZE) { printf ("连接后的字符串过长,无法连接\n" ); return 0 ; } strcpy (result->data, str1->data); strcat (result->data, str2->data); result->length = str1->length + str2->length; return 1 ; }int IndexOf (SString *str, SString *pattern) { for (int i = 0 ; i <= str->length - pattern->length; i++) { int j = 0 ; while (j < pattern->length && str->data[i + j] == pattern->data[j]) { j++; } if (j == pattern->length) { return i; } } return -1 ; }
数组 数组是具有相同类型元素的集合,元素在内存中按顺序存储。根据数组维度的不同,数组可以分为一维数组、二维数组和三维数组。
一维数组 一维数组是一组具有相同类型的变量。数组中的每个元素都有一个索引,索引从0开始。
1 int arr[5 ] = {1 , 2 , 3 , 4 , 5 };
可以通过索引访问和修改数组中的元素:
二维数组 行优先存储
Loc(i,j)=Loc(r,c)+(i−r)×n×xs+(j−c)×xs
列优先存储方式
Loc(i,j)=Loc(r,c)+(j−c)×m×xs+(i−r)×xs
Loc(i, j)
:表示二维数组中元素 A[i][j]
的内存地址。
Loc(r, c)
:表示数组的基地址,也就是 A[r][c]
的内存地址。通常情况下,r
和 c
可以取值为 0
,即 Loc(0, 0)
为基地址。
j - c
:表示 j
列相对于基准列 c
的偏移列数。
m
:数组的行数(每列的元素数)。
xs
:每个元素占据的存储单元大小(例如 int
类型通常为 4
字节)。
i - r
:表示 i
行相对于基准行 r
的偏移行数。
例子
设二维数组a[1..60] [2.70]的基地址为2048,每个元素占2个存储单元,若以行优先顺序存储,元素a[32] [58]的地址是
已知条件
行优先存储寻址公式 在行优先存储中,二维数组的寻址公式为:
Loc(i,j)=Loc(r,c)+(i−r)×n×xs+(j−c)×xs
其中:
base_address = 2048
i = 32
(目标行索引)
j = 58
(目标列索引)
r = 1
(起始行索引)
c = 2
(起始列索引)
n = 69
(每行的列数)
xs = 2
(每个元素的大小)
二维数组可以看作是数组的数组,通常用于表示矩阵或表格数据。声明一个二维数组的方法如下:
1 2 3 4 5 int matrix[3 ][3 ] = { {1 , 2 , 3 }, {4 , 5 , 6 }, {7 , 8 , 9 } };
访问二维数组元素:
1 int value = matrix[1 ][2 ];
矩阵转置 1 2 3 4 5 6 SimpleTranspose(matrix , transpose , 3 , 3 ); printf ("Transposed Matrix:\n" ); PrintMatrix(transpose , 3 , 3 ); return 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 c复制代码#include <stdio.h> #define MAXSIZE 100 typedef struct { int row; int col; int value; } Triple; typedef struct { Triple data [MAXSIZE]; int rows; int cols; int len; } TSMatrix; void FastTranspose(TSMatrix *A, TSMatrix *B) { B->rows = A-> cols; B->cols = A-> rows; B->len = A-> len; int num[A->cols ], position[A-> cols]; for (int i = 0; i < A-> cols; i++) num[i] = 0 ; for (int i = 0; i < A->len ; i++) num[A->data [i].col]++; position[0 ] = 0 ; for (int i = 1; i < A-> cols; i++) { position[i] = position[i - 1 ] + num[i - 1 ]; } for (int i = 0; i < A-> len; i++) { int col = A->data [i].col; int pos = position[col]++; B->data [pos].row = A->data [i].col; B->data [pos].col = A->data [i].row; B->data [pos].value = A->data [i].value; } } void PrintTSMatrix(TSMatrix *M) { printf("row\tcol\tvalue\n" ); for (int i = 0; i < M-> len; i++) { printf ("%d\t%d\t%d\n", M->data [i].row, M->data [i].col, M->data [i].value); } } int main() { TSMatrix A = {{{0 , 0 , 3 }, {0 , 2 , 5 }, {1 , 1 , 7 }, {2 , 0 , 6 }}, 3 , 3 , 4 }; TSMatrix B; FastTranspose(&A, &B); printf("Transposed Sparse Matrix in Triple Form:\n" ); PrintTSMatrix(&B); return 0 ; }
快速转置算法(适用于稀疏矩阵的快速转置)
使用快速转置算法,通过辅助数组 num
和 position
来加速转置过程。
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 c复制代码#include <stdio.h> #define MAXSIZE 100 typedef struct { int row; int col; int value; } Triple; typedef struct { Triple data [MAXSIZE]; int rows; int cols; int len; } TSMatrix; void FastTranspose(TSMatrix *A, TSMatrix *B) { B->rows = A-> cols; B->cols = A-> rows; B->len = A-> len; int num[A->cols ], position[A-> cols]; for (int i = 0; i < A-> cols; i++) num[i] = 0 ; for (int i = 0; i < A->len ; i++) num[A->data [i].col]++; position[0 ] = 0 ; for (int i = 1; i < A-> cols; i++) { position[i] = position[i - 1 ] + num[i - 1 ]; } for (int i = 0; i < A-> len; i++) { int col = A->data [i].col; int pos = position[col]++; B->data [pos].row = A->data [i].col; B->data [pos].col = A->data [i].row; B->data [pos].value = A->data [i].value; } } void PrintTSMatrix(TSMatrix *M) { printf("row\tcol\tvalue\n" ); for (int i = 0; i < M-> len; i++) { printf ("%d\t%d\t%d\n", M->data [i].row, M->data [i].col, M->data [i].value); } } int main() { TSMatrix A = {{{0 , 0 , 3 }, {0 , 2 , 5 }, {1 , 1 , 7 }, {2 , 0 , 6 }}, 3 , 3 , 4 }; TSMatrix B; FastTranspose(&A, &B); printf("Transposed Sparse Matrix in Triple Form:\n" ); PrintTSMatrix(&B); return 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 typedef struct OLNode { int row, col; DataType value; struct OLNode *right, *down; } OLink; typedef struct { OLink *row_head, *col_head; int m, n, len; } CrossList; int CreateCrossList(CrossList *M) { int i, j; OLink p, q; if (M != NULL) free(M); scanf("%d%d%d" , &m, &n, &len); M->m = m; M->n = n; M-> len = len; if ((M-> row_head = (OLink*)malloc(m * sizeof(OLink))) == NULL) exit(OVERFLOW); if ((M-> col_head = (OLink*)malloc(n * sizeof(OLink))) == NULL) exit(OVERFLOW); M->row_head = M-> col_head = NULL; for (scanf("%d%d%d" , &i, &j, &e); i != 0 ; scanf("%d%d%d" , &i, &j, &e)) { if ((p = (OLink*)malloc(sizeof(OLink))) == NULL) exit(OVERFLOW); p ->row = i; p->col = j; p-> value = e; if (M-> row_head == NULL) M->row_head = p; else { for (q = M->row_head ; q->right && q->right ->col < j; q = q-> right); p ->right = q->right ; q-> right = p; } if (M-> col_head == NULL) M->col_head = p; else { for (q = M->col_head ; q->down && q->down ->row < i; q = q-> down); p ->down = q->down ; q-> down = p; } } }
三维数组 三维数组是数组的进一步扩展,通常用于表示多维空间中的数据。
1 2 3 4 5 6 7 8 9 10 11 12 int array3D[2 ][3 ][4 ] = { { {1 , 2 , 3 , 4 }, {5 , 6 , 7 , 8 }, {9 , 10 , 11 , 12 } }, { {13 , 14 , 15 , 16 }, {17 , 18 , 19 , 20 }, {21 , 22 , 23 , 24 } } };
访问三维数组元素:
1 int value = array3D[1 ][2 ][3 ];
矩阵的压缩存储 矩阵压缩存储用于节省空间,特别是在矩阵具有某种特殊结构时,例如对称矩阵、三角矩阵和稀疏矩阵。
对称矩阵 对称矩阵是指矩阵的元素满足 A[i][j] == A[j][i]
。对于对称矩阵,只需要存储矩阵的上三角或下三角部分即可。
假设存储上三角部分:
1 2 3 4 5 6 int n = 3 ; int symmetricMatrix[(n * (n + 1 )) / 2 ]; int index = (i * (i + 1 )) / 2 + j; symmetricMatrix[index] = value;
访问元素时:
1 2 3 4 5 6 7 int getSymmetricMatrixValue (int * mat, int i, int j) { if (i <= j) { return mat[(i * (i + 1 )) / 2 + j]; } else { return mat[(j * (j + 1 )) / 2 + i]; } }
三角矩阵
1 int upperTriangular[(n * (n + 1 )) / 2 ];
存储上三角矩阵的元素:
1 2 3 if (i <= j) { upperTriangular[(i * n) - (i * (i - 1 )) / 2 + (j - i)] = value; }
1 int lowerTriangular[(n * (n + 1 )) / 2 ];
存储下三角矩阵的元素:
1 2 3 if (i >= j) { lowerTriangular[(i * (i + 1 )) / 2 + j] = value; }
稀疏矩阵压缩存储 稀疏矩阵是指大部分元素为零的矩阵。稀疏矩阵可以通过三元组(行、列、值)来压缩存储。常见的存储方法包括COO(Coordinate List)、CSR(Compressed Sparse Row)和CSC(Compressed Sparse Column)。
COO(Coordinate List) :使用三个数组分别存储非零元素的行索引、列索引和对应的值。
1 2 3 int row[] = {0 , 0 , 1 , 2 };int col[] = {0 , 2 , 2 , 3 };int val[] = {10 , 20 , 30 , 40 };
CSR(Compressed Sparse Row) :使用三个数组存储稀疏矩阵,分别是 values
(非零元素的值),col_index
(非零元素所在的列索引)和 row_pointer
(每行开始的位置)。
1 2 3 int values[] = {10 , 20 , 30 , 40 };int col_index[] = {0 , 2 , 2 , 3 };int row_pointer[] = {0 , 2 , 3 , 4 };
CSC(Compressed Sparse Column) :与CSR类似,使用三个数组存储稀疏矩阵,只不过是按列进行存储。
1 2 3 int values[] = {10 , 30 , 20 , 40 };int row_index[] = {0 , 1 , 0 , 2 };int col_pointer[] = {0 , 2 , 3 , 4 };
这些压缩存储方法通过只存储非零元素和它们的位置,大大减少了稀疏矩阵所需的存储空间。
树 树的存储结构主要有三种:双亲表示法 、孩子表示法 和孩子兄弟表示法
A
/ | \
B C D
/ \ |
E F G
双亲表示法—-适合查找节点的父节点,但是在查找孩子节点时效率较低
1 2 3 4 5 6 7 8 9 10 typedef struct { ElementType data; int parent; } PTNode;typedef struct { PTNode nodes[MAX_SIZE]; int n; } PTree;
在双亲表示法中,每个节点记录自己的父节点索引:
索引
数据
父节点索引
0
A
-1
1
B
0
2
C
0
3
D
0
4
E
1
5
F
1
6
G
3
在这个例子中,“A”节点是根节点,因此它的父节点索引为 -1
。每个节点的父节点索引指向它在数组中的位置。
孩子表示法—-适合查找节点的孩子,尤其是树的度较大时
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 typedef struct ChildNode { int child; struct ChildNode *next; } ChildNode;typedef struct { ElementType data; ChildNode *firstChild; } CTNode ;typedef struct { CTNode nodes[MAX_SIZE]; int n; } CTree ;
A → 孩子链表 → B → C → D
B → 孩子链表 → E → F
C → 孩子链表 → (空)
D → 孩子链表 → G
E → 孩子链表 → (空)
F → 孩子链表 → (空)
G → 孩子链表 → (空)
孩子兄弟表示法—-适合同时查找孩子和兄弟节点。此结构将多叉树转换为二叉树,有利于在实际中使用二叉树的算法来处理多叉树
1 2 3 4 5 6 typedef struct CSNode { ElementType data; struct CSNode *firstChild; struct CSNode *nextSibling; } CSNode, *CSTree;
A → firstChild → B
B → firstChild → E, nextSibling → C
C → nextSibling → D
D → firstChild → G
E → nextSibling → F
F → nextSibling → (空)
G → nextSibling → (空)
二叉树 节点数的性质
性质 1 :对任何一棵非空二叉树,第 iii 层上的最多节点数 为 2i−12^{i-1}2i−1(i≥1i \geq 1i≥1)。
例如,第 1 层最多有 21−1=12^{1-1} = 121−1=1 个节点,第 2 层最多有 22−1=22^{2-1} = 222−1=2 个节点,第 3 层最多有 23−1=42^{3-1} = 423−1=4 个节点,依此类推。
性质 2 :深度为 kkk 的二叉树的最多节点数 为 2k−12^k - 12k−1(k≥1k \geq 1k≥1)。
例如,深度为 3 的二叉树最多有 23−1=72^3 - 1 = 723−1=7 个节点。
性质 3 :对任何一棵二叉树,若其叶节点数为 n0n_0n0,度为 2 的节点数为 n2n_2n2,则满足关系式:n0=n2+1n_0 = n_2 + 1n0=n2+1。
这个性质帮助我们快速计算一棵二叉树的叶节点和度为 2 的节点之间的关系。
完全二叉树的性质
性质 4 :如果一棵完全二叉树 有 nnn 个节点,则这些节点可以从上至下、从左到右编号为 1 到 nnn。对于任意节点 iii(1 ≤ iii ≤ nnn):
若 i=1i = 1i=1,则节点 iii 是根节点。
若 i>1i > 1i>1,则节点 iii 的父节点 编号为 ⌊i/2⌋\lfloor i/2 \rfloor⌊i/2⌋。
若 2i≤n2i \leq n2i≤n,则节点 iii 的左孩子 编号为 2i2i2i。
若 2i+1≤n2i + 1 \leq n2i+1≤n,则节点 iii 的右孩子 编号为 2i+12i + 12i+1。
二叉树的深度和高度关系
性质 5 :对于一棵有 nnn 个节点的完全二叉树,其深度(或高度) 为 ⌊log2n⌋+1\lfloor \log_2 n \rfloor + 1⌊log2n⌋+1。
例如,一棵有 7 个节点的完全二叉树的深度为 ⌊log27⌋+1=3\lfloor \log_2 7 \rfloor + 1 = 3⌊log27⌋+1=3。
满二叉树的性质
性质 6 :一棵深度为 kkk 的满二叉树 的节点数为 2k−12^k - 12k−1。
满二叉树指的是每一层都被完全填满的二叉树,没有空缺。
性质 7 :满二叉树的叶节点数 等于度为 2 的节点数加 1,且所有的叶节点都在同一层。
二叉树的节点之间的关系
性质 8 :在二叉树的顺序存储中,父节点、左孩子和右孩子之间的索引关系可以简化操作:
如果某节点的索引为 iii,则其左孩子的索引为 2i2i2i,右孩子的索引为 2i+12i + 12i+1,父节点的索引为 ⌊i/2⌋\lfloor i/2 \rfloor⌊i/2⌋。
性质 9 :在二叉树中,深度越大的层,其节点数呈指数增长。即在二叉树的末端层节点数最多,而根节点只有一个。
二叉树顺序存储
A
/ \
B C
/ \ / \
D E F G
按照顺序存储的方式,这棵树会被存储在数组中如下:
索引
数据
1
A
2
B
3
C
4
D
5
E
6
F
7
G
在这个数组中:
A 的左孩子是 B(索引 2),右孩子是 C(索引 3)。
B 的左孩子是 D(索引 4),右孩子是 E(索引 5)。
C 的左孩子是 F(索引 6),右孩子是 G(索引 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 #include <stdio.h> #define MAX_SIZE 100 typedef char ElementType; typedef struct { ElementType data[MAX_SIZE]; int size; } SeqBinaryTree;void initTree (SeqBinaryTree *tree) { tree->size = 0 ; }void addNode (SeqBinaryTree *tree, ElementType data) { if (tree->size + 1 < MAX_SIZE) { tree->data[++(tree->size)] = data; } else { printf ("二叉树已满,无法插入更多节点。\n" ); } }void preOrderTraverse (SeqBinaryTree *tree, int index) { if (index > tree->size) return ; printf ("%c " , tree->data[index]); preOrderTraverse (tree, 2 * index); preOrderTraverse (tree, 2 * index + 1 ); }int main () { SeqBinaryTree tree; initTree (&tree); addNode (&tree, 'A' ); addNode (&tree, 'B' ); addNode (&tree, 'C' ); addNode (&tree, 'D' ); addNode (&tree, 'E' ); addNode (&tree, 'F' ); addNode (&tree, 'G' ); printf ("前序遍历结果:" ); preOrderTraverse (&tree, 1 ); printf ("\n" ); return 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 #include <stdio.h> #include <stdlib.h> typedef struct TreeNode { int data; struct TreeNode *left ; struct TreeNode *right ; } TreeNode; TreeNode* CreateNode (int value) { TreeNode *node = (TreeNode*)malloc (sizeof (TreeNode)); if (node == NULL ) { printf ("内存分配失败\n" ); exit (1 ); } node->data = value; node->left = NULL ; node->right = NULL ; return node; } TreeNode* Insert (TreeNode *root, int value) { if (root == NULL ) { return CreateNode(value); } if (value < root->data) { root->left = Insert(root->left, value); } else { root->right = Insert(root->right, value); } return root; }void PreorderTraversal (TreeNode *root) { if (root == NULL ) { return ; } printf ("%d " , root->data); PreorderTraversal(root->left); PreorderTraversal(root->right); }void InorderTraversal (TreeNode *root) { if (root == NULL ) { return ; } InorderTraversal(root->left); printf ("%d " , root->data); InorderTraversal(root->right); }void PostorderTraversal (TreeNode *root) { if (root == NULL ) { return ; } PostorderTraversal(root->left); PostorderTraversal(root->right); printf ("%d " , root->data); }int getHeight (TreeNode* root) { if (root == NULL ) { return 0 ; } int leftHeight = getHeight(root->left); int rightHeight = getHeight(root->right); return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1 ; }int getNodeCount (TreeNode* root) { if (root == NULL ) { return 0 ; } return getNodeCount(root->left) + getNodeCount(root->right) + 1 ; }int getLeafNodeCount (TreeNode* root) { if (root == NULL ) { return 0 ; } if (root->left == NULL && root->right == NULL ) { return 1 ; } return getLeafNodeCount(root->left) + getLeafNodeCount(root->right); }int main () { TreeNode *root = NULL ; root = Insert(root, 5 ); root = Insert(root, 3 ); root = Insert(root, 7 ); root = Insert(root, 2 ); root = Insert(root, 4 ); root = Insert(root, 6 ); root = Insert(root, 8 ); printf ("前序遍历: " ); PreorderTraversal(root); printf ("\n" ); printf ("中序遍历: " ); InorderTraversal(root); printf ("\n" ); printf ("后序遍历: " ); PostorderTraversal(root); printf ("\n" ); return 0 ; }
代码解析
节点定义 :
使用结构体 TreeNode
来定义二叉树的节点,每个节点包含一个 data
数据域,以及指向左、右子节点的指针。
创建节点 :
使用 CreateNode(int value)
函数来创建一个新节点。
插入节点 :
使用 Insert(TreeNode *root, int 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 83 84 85 86 87 88 89 90 91 92 93 94 95 96 #include <stdio.h> #include <stdlib.h> #define MaxSize 50 typedef struct BTNode { char data; struct BTNode * lchild ; struct BTNode * rchild ; } BTNode;void CreateBTree (BTNode* bt, char * str) { BTNode* St[MaxSize], *p = NULL ; int top = -1 , k = 0 , j = 0 ; char ch; bt = NULL ; ch = str[j]; while (ch != '\0' ) { switch (ch) { case '(' : top++; St[top] = p; k = 1 ; break ; case ')' : top--; break ; case ',' : k = 2 ; break ; default : p = (BTNode*)malloc (sizeof (BTNode)); p->data = ch; p->lchild = p->rchild = NULL ; if (bt == NULL ) { bt = p; } else { if (k == 1 ) { St[top]->lchild = p; } else if (k == 2 ) { St[top]->rchild = p; } } break ; } j++; ch = str[j]; } }int tree_height (TreeNode* root) { if (root == NULL ) { return 0 ; } int left_height = tree_height(root->left); int right_height = tree_height(root->right); return (left_height > right_height) ? (left_height +1 ) : (right_height + 1 ); }int count_nodes (TreeNode* root) { if (root == NULL ) { return 0 ; } return count_nodes(root->left) + count_nodes(root->right) + 1 ; }int count_leaves (TreeNode* root) { if (root == NULL ) { return 0 ; } if (root->left == NULL && root->right == NULL ) { return 1 ; } return count_leaves(root->left) + count_leaves(root->right); }void print_tree (TreeNode* root) { if (root == NULL ) { return ; } printf ("%d" , root->value); if (root->left || root->right) { printf ("(" ); print_tree(root->left); printf ("," ); print_tree(root->right); printf (")" ); } }
二叉树的种类
二叉查找树(BST) :左子树节点值小于根节点,右子树节点值大于根节点,且左右子树分别也是二叉查找树。
平衡二叉树(AVL树) :任意节点的左右子树高度差不超过1,保证二叉查找树的平衡性,从而提高查找效率。
完全二叉树 :除最后一层外,其他层节点都达到最大数目,且最后一层的节点从左向右连续排列。
满二叉树 :所有的非叶节点都有两个子节点,且所有叶节点在同一层。
应用场景
表达式解析 :二叉树可用于表达式的解析和计算,如中缀表达式、前缀表达式、后缀表达式的解析。
查找和排序 :二叉查找树适合快速查找数据。
数据存储 :在数据库的索引、文件系统的目录结构中,二叉树是常见的存储结构。
图
顶点(Vertex) :图中的节点。
边(Edge) :连接两个顶点的线段,表示两个顶点之间的关系。
有向图(Directed Graph) :图中的边是有方向的。
无向图(Undirected Graph) :图中的边是无方向的。
邻接 :如果两个顶点之间有一条边连接,则称它们是相邻的。
邻接矩阵(Adjacency Matrix) :使用矩阵表示图。
邻接表(Adjacency List) :使用链表表示图。
在C语言中,通常使用邻接矩阵 和邻接表 来表示图结构。
图的表示方法 1. 邻接矩阵表示法 邻接矩阵是一种二维数组,矩阵中的元素表示顶点之间是否有边。对于一个包含 n
个顶点的图,我们可以使用一个 n × n
的二维数组来表示:
如果有边 i -> j
,则 adj[i][j] = 1
(或权重)。
如果没有边 i -> j
,则 adj[i][j] = 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 #define MAX_VERTICES 100 #define INF 9999 int adjMatrix[MAX_VERTICES][MAX_VERTICES];void initAdjMatrix (int n) { for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < n; j++) { adjMatrix[i][j] = (i == j) ? 0 : INF; } } }void addEdge (int u, int v, int weight) { adjMatrix[u][v] = weight; adjMatrix[v][u] = weight; }void AddEdge (Graph *g, int src, int dest) { g->adjMatrix[src][dest] = 1 ; g->adjMatrix[dest][src] = 1 ; }void PrintGraph (Graph *g) { printf ("邻接矩阵:\n" ); for (int i = 0 ; i < g->numVertices; i++) { for (int j = 0 ; j < g->numVertices; j++) { printf ("%d " , g->adjMatrix[i][j]); } printf ("\n" ); } }int main () { Graph g; InitGraph (&g, 5 ); AddEdge (&g, 0 , 1 ); AddEdge (&g, 0 , 4 ); AddEdge (&g, 1 , 2 ); AddEdge (&g, 1 , 3 ); AddEdge (&g, 1 , 4 ); AddEdge (&g, 2 , 3 ); AddEdge (&g, 3 , 4 ); PrintGraph (&g); return 0 ; }
2. 邻接表表示法 邻接表使用链表数组 来表示图,每个顶点有一个链表,该链表中存储与该顶点相邻的所有顶点。邻接表适用于边数较少的稀疏图 ,可以节省内存。
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 #include <stdio.h> #include <stdlib.h> #define MAX_VERTICES 100 typedef struct AdjListNode { int vertex; int weight; struct AdjListNode * next; } AdjListNode;typedef struct { AdjListNode* head; } AdjList;typedef struct { int numVertices; AdjList adjLists[MAX_VERTICES]; } Graph;AdjListNode* createNode (int vertex, int weight) { AdjListNode* newNode = (AdjListNode*)malloc (sizeof (AdjListNode)); newNode->vertex = vertex; newNode->weight = weight; newNode->next = NULL ; return newNode; }void initGraph (Graph* graph, int numVertices) { graph->numVertices = numVertices; for (int i = 0 ; i < numVertices; i++) { graph->adjLists[i].head = NULL ; } }void addEdge (Graph* graph, int src, int dest, int weight) { AdjListNode* newNode = createNode (dest, weight); newNode->next = graph->adjLists[src].head; graph->adjLists[src].head = newNode; newNode = createNode (src, weight); newNode->next = graph->adjLists[dest].head; graph->adjLists[dest].head = newNode; }
图的常见操作
遍历(Traversal) :
深度优先遍历(DFS) :从某个顶点开始尽可能深地访问图中的节点。
广度优先遍历(BFS) :从某个顶点开始,逐层访问图中的节点。
最短路径 :
Dijkstra算法 :用于求解有向图中单源最短路径。
Floyd算法 :用于求解任意两点之间的最短路径。
连通性 :
最小生成树(MST) :
Prim算法 :构建最小生成树。
Kruskal算法 :构建最小生成树
深度优先遍历(DFS)
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 void DFS (Graph* graph, int vertex, bool visited[MAX_VERTICES]) { visited[vertex] = true ; printf ("%d " , vertex); AdjListNode* temp = graph->adjLists[vertex].head; while (temp) { if (!visited[temp->vertex]) { DFS (graph, temp->vertex, visited); } temp = temp->next; } }int stack[MAX_VERTICES];int top = -1 ;void push (int vertex) { stack[++top] = vertex; }int pop () { return stack[top--]; }bool isStackEmpty () { return top == -1 ; }void DFS_Stack (Graph* graph, int startVertex) { bool visited[MAX_VERTICES] = {false }; push (startVertex); visited[startVertex] = true ; while (!isStackEmpty ()) { int vertex = pop (); printf ("%d " , vertex); AdjListNode* temp = graph->adjLists[vertex].head; while (temp) { if (!visited[temp->vertex]) { push (temp->vertex); visited[temp->vertex] = true ; } temp = temp->next; } } }
广度优先遍历(BFS)
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 #define MAX_QUEUE_SIZE 100 int queue[MAX_QUEUE_SIZE];int front = 0 , rear = 0 ;void enqueue (int vertex) { queue[rear++] = vertex; }int dequeue () { return queue[front++]; }bool isEmpty () { return front == rear; }void BFS (Graph* graph, int startVertex) { bool visited[MAX_VERTICES] = {false }; enqueue (startVertex); visited[startVertex] = true ; while (!isEmpty ()) { int vertex = dequeue (); printf ("%d " , vertex); AdjListNode* temp = graph->adjLists[vertex].head; while (temp) { if (!visited[temp->vertex]) { enqueue (temp->vertex); visited[temp->vertex] = true ; } temp = temp->next; } } }
最小生成树(MST) 对于一个连接图 ,最小生成树 是图中所有顶点的一个子集,它包括图中所有的顶点,并且是一个无环 的树。最小生成树的边的总权重是最小的。
算法:
Prim 算法
Kruskal 算法
Prim 算法
Prim 算法 通过从一个顶点开始,逐步将最小的边加入生成树,直到所有顶点都在树中。每次都选择当前生成树与剩余顶点之间的最小边。
Prim 算法的步骤 :
从任意一个顶点(例如顶点 0)开始,标记为已访问。
每次选择一个已经加入生成树的顶点,通过它与尚未加入树的顶点之间的最小边,将一个新顶点加入生成树。
重复步骤 2,直到所有顶点都加入生成树。
数据结构 :
邻接矩阵 或邻接表 来存储图。
最小堆 或优先队列 用于高效地选择最小边。
Prim 算法(使用邻接矩阵实现) :
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 #include <stdio.h> #include <stdbool.h> #include <limits.h> #define MAX_VERTICES 100 int graph[MAX_VERTICES][MAX_VERTICES]; int parent[MAX_VERTICES]; int key[MAX_VERTICES]; int minKey (int key[], bool mstSet[], int numVertices) { int min = INT_MAX, minIndex; for (int v = 0 ; v < numVertices; v++) { if (!mstSet[v] && key[v] < min) { min = key[v]; minIndex = v; } } return minIndex; }void primMST (int numVertices) { bool mstSet[MAX_VERTICES]; for (int i = 0 ; i < numVertices; i++) { key[i] = INT_MAX; mstSet[i] = false ; } key[0 ] = 0 ; parent[0 ] = -1 ; for (int count = 0 ; count < numVertices - 1 ; count++) { int u = minKey (key, mstSet, numVertices); mstSet[u] = true ; for (int v = 0 ; v < numVertices; v++) { if (graph[u][v] && !mstSet[v] && graph[u][v] < key[v]) { key[v] = graph[u][v]; parent[v] = u; } } } printf ("Edge \tWeight\n" ); for (int i = 1 ; i < numVertices; i++) { printf ("%d - %d \t%d\n" , parent[i], i, graph[i][parent[i]]); } }int main () { int numVertices = 5 ; int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 2 , 0 , 6 , 0 }, {2 , 0 , 3 , 8 , 5 }, {0 , 3 , 0 , 0 , 7 }, {6 , 8 , 0 , 0 , 9 }, {0 , 5 , 7 , 9 , 0 } }; primMST (numVertices); return 0 ; }
Kruskal 算法
Kruskal 算法 是另一种常见的最小生成树算法,它通过边的方式来构建生成树。算法的核心思想是按边的权重从小到大排序 ,然后逐一添加边到生成树中,前提是不会形成环(使用并查集判断是否形成环)。
Kruskal 算法的步骤 :
将图中的所有边按权重升序排列。
使用 并查集(Union-Find) 来判断两个顶点是否属于同一个连通分量。
按照权重从小到大的顺序,依次添加边到生成树中,若添加的边不会形成环,则将边加入生成树。
重复上述步骤直到生成树包含 V−1V - 1V−1 条边。
数据结构 :
边列表 :存储图的所有边。
并查集(Union-Find) :用于判断是否形成环。
Kruskal 算法(使用边列表和并查集实现) :
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 #include <stdio.h> #include <stdlib.h> #define MAX_VERTICES 100 typedef struct { int u, v, weight; } Edge;int find (int parent[], int i) { if (parent[i] == -1 ) return i; return find (parent, parent[i]); }void unionSets (int parent[], int x, int y) { int xroot = find (parent, x); int yroot = find (parent, y); if (xroot != yroot) parent[xroot] = yroot; }int compare (const void * a, const void * b) { return ((Edge*)a)->weight - ((Edge*)b)->weight; }void kruskalMST (Edge edges[], int numEdges, int numVertices) { qsort (edges, numEdges, sizeof (Edge), compare); int parent[MAX_VERTICES]; for (int i = 0 ; i < numVertices; i++) parent[i] = -1 ; printf ("Edge \tWeight\n" ); int mstWeight = 0 ; for (int i = 0 ; i < numEdges; i++) { int u = edges[i].u; int v = edges[i].v; int weight = edges[i].weight; int setU = find (parent, u); int setV = find (parent, v); if (setU != setV) { printf ("%d - %d \t%d\n" , u, v, weight); unionSets (parent, setU, setV); mstWeight += weight; } } printf ("Total weight of MST: %d\n" , mstWeight); }int main () { int numVertices = 4 ; int numEdges = 5 ; Edge edges[] = { {0 , 1 , 10 }, {0 , 2 , 6 }, {0 , 3 , 5 }, {1 , 3 , 15 }, {2 , 3 , 4 } }; kruskalMST (edges, numEdges, numVertices); return 0 ; }
最短路径算法
Dijkstra 算法
Dijkstra 算法 是一个贪心算法,用于计算一个图中单源最短路径的问题,即从一个起始节点出发,计算到所有其他节点的最短路径。该算法适用于图中没有负权边的情况。
Dijkstra 算法的步骤:
初始化一个距离数组 dist[]
,用来存储源节点到各个节点的最短距离。起始节点的距离设置为 0,其他节点的距离设置为无限大。
使用一个优先队列(或最小堆)来选取当前未处理的、具有最小 dist[]
值的节点。
从当前节点出发,更新所有相邻节点的最短距离,如果发现更小的距离,则更新。
重复以上步骤直到所有节点都被访问过。
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 #include <stdio.h> #include <limits.h> #define MAX_VERTICES 100 #define INF INT_MAX int graph[MAX_VERTICES][MAX_VERTICES]; int dist[MAX_VERTICES]; bool visited[MAX_VERTICES]; void dijkstra (int numVertices, int start) { for (int i = 0 ; i < numVertices; i++) { dist[i] = INF; visited[i] = false ; } dist[start] = 0 ; for (int count = 0 ; count < numVertices - 1 ; count++) { int u = -1 , minDist = INF; for (int v = 0 ; v < numVertices; v++) { if (!visited[v] && dist[v] < minDist) { u = v; minDist = dist[v]; } } visited[u] = true ; for (int v = 0 ; v < numVertices; v++) { if (graph[u][v] && !visited[v] && dist[u] + graph[u][v] < dist[v]) { dist[v] = dist[u] + graph[u][v]; } } } printf ("Vertex \t Distance from source\n" ); for (int i = 0 ; i < numVertices; i++) { printf ("%d \t %d\n" , i, dist[i]); } }int main () { int numVertices = 5 ; int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 10 , 0 , 30 , 100 }, {10 , 0 , 50 , 0 , 0 }, {0 , 50 , 0 , 20 , 10 }, {30 , 0 , 20 , 0 , 60 }, {100 , 0 , 10 , 60 , 0 } }; dijkstra (numVertices, 0 ); return 0 ; }
Floyd-Warshall 算法
Floyd-Warshall 算法的步骤:
初始化一个距离矩阵 dist[][]
,将所有边的权值赋给相应位置。如果没有边,赋值为无限大。
对于每一对顶点 i,ji, ji,j,检查是否有一个中间顶点 kkk,使得通过 kkk 达到 jjj 的路径比直接从 iii 到 jjj 的路径更短。如果是,则更新 dist[i][j]
。
重复上述过程,直到找到所有顶点对的最短路径。
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 #include <stdio.h> #include <limits.h> #define MAX_VERTICES 100 #define INF INT_MAX int graph[MAX_VERTICES][MAX_VERTICES]; void floydWarshall (int numVertices) { int dist[MAX_VERTICES][MAX_VERTICES]; for (int i = 0 ; i < numVertices; i++) { for (int j = 0 ; j < numVertices; j++) { if (i == j) dist[i][j] = 0 ; else if (graph[i][j]) dist[i][j] = graph[i][j]; else dist[i][j] = INF; } } for (int k = 0 ; k < numVertices; k++) { for (int i = 0 ; i < numVertices; i++) { for (int j = 0 ; j < numVertices; j++) { if (dist[i][j] > dist[i][k] + dist[k][j]) { dist[i][j] = dist[i][k] + dist[k][j]; } } } } printf ("Shortest distances between every pair of vertices:\n" ); for (int i = 0 ; i < numVertices; i++) { for (int j = 0 ; j < numVertices; j++) { if (dist[i][j] == INF) printf ("INF " ); else printf ("%d " , dist[i][j]); } printf ("\n" ); } }int main () { int numVertices = 4 ; int graph[MAX_VERTICES][MAX_VERTICES] = { {0 , 3 , INF, INF}, {3 , 0 , 1 , INF}, {INF, 1 , 0 , 7 }, {INF, INF, 7 , 0 } }; floydWarshall (numVertices); return 0 ; }
AOV网和拓扑排序 入度法
思路 :
计算所有节点的入度。
将入度为 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 #include <stdio.h> #include <stdbool.h> #define MAX_VERTICES 100 int graph[MAX_VERTICES][MAX_VERTICES]; int inDegree[MAX_VERTICES]; int numVertices; void topologicalSort () { int queue[MAX_VERTICES], front = 0 , rear = 0 ; for (int i = 0 ; i < numVertices; i++) { if (inDegree[i] == 0 ) { queue[rear++] = i; } } int sortedCount = 0 ; while (front < rear) { int u = queue[front++]; printf ("%d " , u); sortedCount++; for (int v = 0 ; v < numVertices; v++) { if (graph[u][v] == 1 ) { inDegree[v]--; if (inDegree[v] == 0 ) { queue[rear++] = v; } } } } if (sortedCount != numVertices) { printf ("\n图中存在环,无法进行拓扑排序。\n" ); } }int main () { numVertices = 6 ; int g[MAX_VERTICES][MAX_VERTICES] = { {0 , 1 , 1 , 0 , 0 , 0 }, {0 , 0 , 0 , 1 , 0 , 0 }, {0 , 0 , 0 , 1 , 0 , 0 }, {0 , 0 , 0 , 0 , 1 , 1 }, {0 , 0 , 0 , 0 , 0 , 1 }, {0 , 0 , 0 , 0 , 0 , 0 } }; for (int i = 0 ; i < numVertices; i++) { for (int j = 0 ; j < numVertices; j++) { graph[i][j] = g[i][j]; } } for (int i = 0 ; i < numVertices; i++) { inDegree[i] = 0 ; for (int j = 0 ; j < numVertices; j++) { if (graph[j][i] == 1 ) { inDegree[i]++; } } } printf ("拓扑排序结果:\n" ); topologicalSort (); return 0 ; }
深度优先搜索法实现拓扑排序
对图进行深度优先搜索(DFS)。
在回溯过程中,将节点压入栈中。
最终栈中的节点顺序即为拓扑排序的结果(栈顶到栈底)。
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 #include <stdio.h> #include <stdbool.h> #define MAX_VERTICES 100 int graph[MAX_VERTICES][MAX_VERTICES]; bool visited[MAX_VERTICES]; int numVertices; void dfs (int u, int stack[], int *top) { visited[u] = true ; for (int v = 0 ; v < numVertices; v++) { if (graph[u][v] == 1 && !visited[v]) { dfs (v, stack, top); } } stack[(*top)++] = u; }void topologicalSort () { int stack[MAX_VERTICES]; int top = 0 ; for (int i = 0 ; i < numVertices; i++) { visited[i] = false ; } for (int i = 0 ; i < numVertices; i++) { if (!visited[i]) { dfs (i, stack, &top); } } printf ("拓扑排序结果:\n" ); for (int i = top - 1 ; i >= 0 ; i--) { printf ("%d " , stack[i]); } printf ("\n" ); }int main () { numVertices = 6 ; int g[MAX_VERTICES][MAX_VERTICES] = { {0 , 1 , 1 , 0 , 0 , 0 }, {0 , 0 , 0 , 1 , 0 , 0 }, {0 , 0 , 0 , 1 , 0 , 0 }, {0 , 0 , 0 , 0 , 1 , 1 }, {0 , 0 , 0 , 0 , 0 , 1 }, {0 , 0 , 0 , 0 , 0 , 0 } }; for (int i = 0 ; i < numVertices; i++) { for (int j = 0 ; j < numVertices; j++) { graph[i][j] = g[i][j]; } } topologicalSort (); return 0 ; }
图 是一种重要的数据结构,主要用于表示对象之间的关系,可以分为有向图和无向图。
在C语言中,图通常使用邻接矩阵 或邻接表 来表示,邻接矩阵适用于稠密图 ,邻接表适用于稀疏图 。
常见的图操作包括深度优先遍历(DFS) 、广度优先遍历(BFS) 、求最短路径、连通性等。
选择合适的图表示方式和遍历方法可以提高程序的效率和可读性。
排序
二叉排序树
插入操作(Insert)
插入操作的目的是将新节点插入到树中的合适位置。插入过程如下:
从根节点开始,比较要插入的值与当前节点的值。
如果新值小于当前节点的值,则递归插入到左子树;如果新值大于当前节点的值,则递归插入到右子树。
如果当前节点的左子树或右子树为空,则直接插入节点。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 typedef struct TreeNode { int data ; struct TreeNode *left, *right; } TreeNode; TreeNode* insert(TreeNode* root, int value) { if (root == NULL) { TreeNode* newNode = (TreeNode*)malloc(sizeof(TreeNode)); newNode ->data = value; newNode ->left = newNode-> right = NULL; return newNode; } if (value < root->data ) { root ->left = insert(root-> left, value); } else if (value > root->data ) { root ->right = insert(root-> right, value); } return root; }
查找操作(Search)
查找操作用于判断某个值是否在二叉排序树中。查找的过程与插入类似,从根节点开始,依次向左或向右子树移动,直到找到目标节点或到达叶子节点。
1 2 3 4 5 6 7 8 9 10 TreeNode* search(TreeNode* root, int value) { if (root == NULL || root->data == value) { return root; } if (value < root->data ) { return search(root-> left, value); } else { return search(root-> right, value); } }
删除操作(Delete)
删除操作有三种情况:
删除叶子节点 :直接删除该节点。
删除只有一个子节点的节点 :将该节点的父节点指向该节点的唯一子节点。
删除有两个子节点的节点 :找到该节点的中序后继(右子树中最小的节点)或中序前驱(左子树中最大的节点),用它替换该节点,然后删除中序后继或前驱节点。
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 TreeNode* minValueNode(TreeNode* node) { TreeNode* current = node; while (current && current-> left != NULL) { current = current-> left; } return current; } TreeNode* deleteNode(TreeNode* root, int value) { if (root == NULL) return root; if (value < root->data ) { root ->left = deleteNode(root-> left, value); } else if (value > root->data ) { root ->right = deleteNode(root-> right, value); } else { if (root-> left == NULL) { TreeNode * temp = root-> right; free(root); return temp; } else if (root-> right == NULL) { TreeNode * temp = root-> left; free(root); return temp; } TreeNode * temp = minValueNode(root-> right); root ->data = temp->data ; root ->right = deleteNode(root->right , temp->data ); } return root; }
4. 中序遍历(Inorder Traversal) 中序遍历 可以按升序输出二叉排序树中的所有节点。在二叉排序树中,中序遍历的结果总是递增的 。
1 2 3 4 5 6 7 void inorder (TreeNode* root) { if (root != NULL) { inorder (root->left); printf ("%d ", root->data); inorder (root->right); } }
5. 前序遍历(Preorder Traversal) 前序遍历 的顺序是:先访问根节点,再遍历左子树,最后遍历右子树。
1 2 3 4 5 6 7 void preorder (TreeNode* root) { if (root != NULL) { printf ("%d ", root->data); preorder (root->left); preorder (root->right); } }
6. 后序遍历(Postorder Traversal) 后序遍历 的顺序是:先遍历左子树,再遍历右子树,最后访问根节点。
1 2 3 4 5 6 7 void postorder (TreeNode* root) { if (root != NULL) { postorder (root->left); postorder (root->right); printf ("%d ", root->data); } }