当前位置:   article > 正文

数据结构(C语言版)-第二章线性表

数据结构(C语言版)-第二章线性表

线性数据结构的特点

线性结构是数据结构中的一种基本类型,其特点是数据元素之间存在一对一的线性关系,即每个元素(除了第一个和最后一个)都有一个前驱元素和一个后继元素。线性结构的主要特点包括:

1. 顺序性:线性结构中的元素按照一定的顺序排列,这个顺序可以是逻辑上的也可以是物理存储上的顺序。
2. 访问方式:线性结构支持顺序访问和随机访问(如果数据是连续存储的话)。顺序访问是指从结构的一个端点开始,逐个访问每一个元素直到另一个端点。对于某些线性结构,如数组,还可以通过索引直接访问任意位置的元素。
3. 操作简单:线性结构的基本操作如插入、删除、查找等通常相对容易实现,尤其是当数据是顺序存储时。
4. 结构清晰:线性结构的逻辑关系直观明了,便于理解和操作。

常见的线性结构包括:

- 数组 (Array): 元素在内存中连续存储,每个元素可以通过索引快速访问,但插入和删除操作可能较慢,因为可能需要移动大量元素。
- 链表 (Linked List): 元素在内存中可以不连续存储,每个元素包含指向下一个元素的指针(单链表),或者同时包含指向前一个和后一个元素的指针(双链表)。链表的插入和删除操作较快,但随机访问较慢。
- 栈 (Stack): 后进先出(LIFO, Last In First Out)的线性结构,主要操作是压栈(push)和弹栈(pop)。
- 队列 (Queue): 先进先出(FIFO, First In First Out)的线性结构,主要操作是入队(enqueue)和出队(dequeue)。
- 循环队列 (Circular Queue): 队列的一种变体,尾部连接到头部形成环状,可以更高效地利用存储空间。
- 字符串 (String): 一种特殊的线性结构,元素为字符,常用于文本处理。
线性结构因其简单性和普遍适用性,在算法和数据结构中占据重要地位。

2. 1 线性表的类型定义

ADT List {

    数据对象: D={a_{i}| a_{i}\in ElemSet, i=1,2,...,n, n\geqslant 0}

    数据关系: R1 = {<a_{a-1}, a_{i} >|a_{i-1}, a_{i}\in D,i=2,..,n}

基本操作:

        InitList(&L)

                操作结果:构造一个空的线性表L

        DestroyList(&L)

                初始条件:线性表L已存在。

                操作结果,销毁线性表L。

        ClearList (&L)

                初始条件:线性表L巳存在。

                操作结果:将L重置为空表。

        ListEmpty(L)

                初始条件:线性表L已存在。

                操作结果:若L为空表,则返回 TRUE, 否则返回 FALSE。

        ListLength(L)

                初始条件:线性表L已存在。

                操作结果:返回L中数据元素个数。

        GetElem(L, i, &e)

                初始条件:线性表 已存在, 1 \leq i \leqslant ListLength(L)

                操作结果:用e返回L中第i个数据元素的值。

        LocateElem(L, e, compare())

                初始条件:线性表L已存在, compare() 是数据元素判定函数。

                操作结果:返回 中第 个与 满足关系 compare() 的数据元素的位序。若这样的数据元素不存在,则返回值为 o.

        PriorElem(L, cur-e, &pre-e)

                初始条件:线性表L已存在。

                操作结果:若 cur-e 的数据元素,且不是第一个,则用 pre-e返回它的前驱,否则操作失败, pre_ 无定义。

        NextElem(L, cur-e, &next-e)

                初始条件:线性表L已存在。

                操作结果:若 cur-e是L的数据元素,且不是最后一个,则用 next-e 返回它的后继,否则操作失败, next-e无定义。

        Listinser (&L, i, e)

                初始条件:线性表L已存在, l \leq  i \leq  ListLength(L) +1。

                操作结果,在L中第i个位置之前插入新的数据元素 e,L 的长度加1。

        ListDelete(&L, i, &e)

                初始条件:线性表L已存在且非空, l \leq  i  \leq  ListLength(L)

                操作结果:删除L的第 个数据元素,并用e返回其值,L的长度减1。

        ListTraverse(L, visit())

                初始条件:线性表L已存在。

                操作结果:依次对L的每个数据元素调用函数 visit()。一旦 visit() 失败,则操作失败。

} ADT List

        

例2-1 求两个集合的并集

假设利用两个线性表 LA LB 分别表示两个集合 B( 即线性表中的数 据元素即为集合中的成员),现要求一个新的集合 A=AUB 。这就要求对线性表作如下 操作:扩大线性表 LA, 将存在千线性表 LB 中而不存在于线性表 LA 中的数据元素插入 到线性表 LA 中去。只要从线性表 LB 中依次取得每个数据元素,并依值在线性表 LA 进行查访,若不存在,则插入之。上述操作过程可用下列算法描述之。
  1. #include <stdio.h>
  2. #define MAX_ELEMENTS 100 // 假设最大元素数量不超过这个值
  3. // 函数用于打印集合
  4. void printArray(int arr[], int size) {
  5. for(int i = 0; i < size; ++i)
  6. printf("%d ", arr[i]);
  7. printf("\n");
  8. }
  9. // 函数用于求两个线性表的并集
  10. int unionOfArrays(int LA[], int sizeLA, int LB[], int sizeLB, int result[]) {
  11. int indexResult = 0; // 并集数组当前索引
  12. int i, j;
  13. // 将LA数组的所有元素加入到result中
  14. for(i = 0; i < sizeLA; ++i)
  15. result[indexResult++] = LA[i];
  16. // 检查LB中的每个元素是否已存在于result中,若不存在则添加
  17. for(i = 0; i < sizeLB; ++i) {
  18. int exists = 0;
  19. for(j = 0; j < indexResult; ++j) {
  20. if(LB[i] == result[j]) {
  21. exists = 1;
  22. break;
  23. }
  24. }
  25. if(!exists) // 如果LB中的元素不在result中,则加入
  26. result[indexResult++] = LB[i];
  27. }
  28. return indexResult; // 返回并集数组的实际大小
  29. }
  30. int main() {
  31. int LA[] = {1, 2, 3, 4};
  32. int LB[] = {3, 4, 5, 6};
  33. int A[MAX_ELEMENTS]; // 用于存放并集
  34. int sizeLA = sizeof(LA)/sizeof(LA[0]);
  35. int sizeLB = sizeof(LB)/sizeof(LB[0]);
  36. int unionSize = unionOfArrays(LA, sizeLA, LB, sizeLB, A);
  37. printf("Union of LA and LB is: ");
  38. printArray(A, unionSize);
  39. return 0;
  40. }

原文抽象代码

算法2.1

例2-2 线性表合并有序排列

        已知线性表 LA LB 中的数据元素按值非递减有序排列,现要求将 LA, LB 归并为一个新的线性表 LC, LC 中的数据元素仍按值非递减有序排列。例如,设
LA= (3,5,8,11)
LB= (2,6,8,9,ll,15,20)
LC= (2,3,5,6,8,8,9,ll,ll,15,20)
  1. #include <stdio.h>
  2. #define MAX_ELEMENTS 100
  3. // 函数用于打印线性表
  4. void printList(int list[], int size)
  5. {
  6. for (int i = 0; i < size; ++i)
  7. printf("%d ", list[i]);
  8. printf("\n");
  9. }
  10. // 函数用于合并两个有序线性表为一个新的有序线性表
  11. int mergeSortedLists(int LA[], int sizeLA, int LB[], int sizeLB, int LC[])
  12. {
  13. int i = 0, j = 0, k = 0; // 初始化三个指针
  14. // 遍历两个列表,直到一个列表结束
  15. while (i < sizeLA && j < sizeLB)
  16. {
  17. if (LA[i] <= LB[j])
  18. {
  19. LC[k++] = LA[i++];
  20. }
  21. else
  22. {
  23. LC[k++] = LB[j++];
  24. }
  25. }
  26. // 如果LA还有剩余元素,直接拷贝到LC
  27. while (i < sizeLA)
  28. {
  29. LC[k++] = LA[i++];
  30. }
  31. // 如果LB还有剩余元素,直接拷贝到LC
  32. while (j < sizeLB)
  33. {
  34. LC[k++] = LB[j++];
  35. }
  36. return k; // 返回新列表的实际大小
  37. }
  38. int main()
  39. {
  40. int LA[] = {3, 5, 8, 11};
  41. int LB[] = {2, 6, 8, 9, 11, 15, 20};
  42. int LC[MAX_ELEMENTS]; // 假设MAX_ELEMENTS足够大以容纳合并后的列表
  43. int sizeLA = sizeof(LA) / sizeof(LA[0]);
  44. int sizeLB = sizeof(LB) / sizeof(LB[0]);
  45. int newSize = mergeSortedLists(LA, sizeLA, LB, sizeLB, LC);
  46. printf("Merged list LC is: ");
  47. printList(LC, newSize);
  48. return 0;
  49. }

原文抽象代码

算法2.2

2. 2 线性表的顺序表示和实现

算法2.3-扩容添加元素

例如,图 2.3 表示一个线性表在进行插入操作的前、后,其数据元素在存储空间中的位置变化。为了在线性表的第 和第 个元素之间插入一个值为 25 的数据元素,则需将第五个至第 个数据元素依次往后移动一个位置。

        一般情况下,在第 i(l~i~n) 个元素之前插入一个元素时,需将第 至第 i( n-i+ 1) 个元素向后移动一个位置,如算法 2.4 所示。

可执行代码案例

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h> // 用于memset
  4. typedef struct
  5. {
  6. int *data; // 动态数组
  7. int capacity; // 当前容量
  8. int size; // 当前元素数量
  9. } LinearList;
  10. // 初始化线性表
  11. LinearList *initLinearList()
  12. {
  13. LinearList *list = (LinearList *)malloc(sizeof(LinearList));
  14. list->data = (int *)calloc(10, sizeof(int)); // 初始容量设为10
  15. list->capacity = 10;
  16. list->size = 0;
  17. return list;
  18. }
  19. // 销毁线性表
  20. void destroyLinearList(LinearList *list)
  21. {
  22. free(list->data);
  23. free(list);
  24. }
  25. // 扩容函数
  26. void resizeLinearList(LinearList *list)
  27. {
  28. int newCapacity = list->capacity * 2; // 扩容为原来的两倍
  29. list->data = (int *)realloc(list->data, newCapacity * sizeof(int));
  30. list->capacity = newCapacity;
  31. }
  32. void insertAt(LinearList *list, int index, int value)
  33. {
  34. if (list->size == list->capacity)
  35. {
  36. resizeLinearList(list); // 先检查是否需要扩容
  37. }
  38. // 特殊处理:如果要插入到空列表或索引0,直接添加在最前面
  39. if (list->size == 0 || index == 0)
  40. {
  41. memmove(&list->data[1], &list->data[0], list->size * sizeof(int)); // 移动现有元素
  42. list->data[0] = value; // 插入新元素
  43. }
  44. else if (index <= list->size)
  45. { // 对于其他合法索引
  46. memmove(&list->data[index + 1], &list->data[index], (list->size - index) * sizeof(int)); // 移动元素
  47. list->data[index] = value; // 插入新元素
  48. }
  49. else
  50. {
  51. printf("Index out of bounds.\n");
  52. return;
  53. }
  54. list->size++; // 无论哪种情况,插入后元素数量都要增加
  55. }
  56. // 打印线性表
  57. void printLinearList(const LinearList *list)
  58. {
  59. for (int i = 0; i < list->size; ++i)
  60. {
  61. printf("%d ", list->data[i]);
  62. }
  63. printf("\n");
  64. }
  65. int main()
  66. {
  67. LinearList *list = initLinearList();
  68. // 添加更多元素以展示扩容
  69. for (int i = 0; i < 20; ++i)
  70. {
  71. insertAt(list, list->size, i);
  72. }
  73. // 示例:在索引为1的位置插入数字7
  74. insertAt(list, 1, 77);
  75. printLinearList(list); // 打印线性表
  76. destroyLinearList(list);
  77. return 0;
  78. }

算法2. 4 -固定大小的线性表删除元素

算法2.5 (略)

算法2.6 (顺序表合并)

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h> // 用于memset
  4. // 函数声明
  5. void mergeSortedArrays(int arr1[], int size1, int arr2[], int size2, int mergedArr[]);
  6. void printArray(int arr[], int size);
  7. int main() {
  8. int arr1[] = {1, 3, 5, 7};
  9. int arr2[] = {2, 4, 6, 8};
  10. int size1 = sizeof(arr1) / sizeof(arr1[0]);
  11. int size2 = sizeof(arr2) / sizeof(arr2[0]);
  12. int mergedSize = size1 + size2;
  13. int mergedArr[mergedSize];
  14. mergeSortedArrays(arr1, size1, arr2, size2, mergedArr);
  15. printf("Merged array: ");
  16. printArray(mergedArr, mergedSize);
  17. return 0;
  18. }
  19. // 合并两个有序数组的函数
  20. void mergeSortedArrays(int arr1[], int size1, int arr2[], int size2, int mergedArr[]) {
  21. int i = 0, j = 0, k = 0;
  22. // 遍历两个数组,直到一个数组遍历完
  23. while (i < size1 && j < size2) {
  24. if (arr1[i] <= arr2[j]) {
  25. mergedArr[k++] = arr1[i++];
  26. } else {
  27. mergedArr[k++] = arr2[j++];
  28. }
  29. }
  30. // 如果arr1还有剩余元素,直接复制到mergedArr
  31. while (i < size1) {
  32. mergedArr[k++] = arr1[i++];
  33. }
  34. // 如果arr2还有剩余元素,直接复制到mergedArr
  35. while (j < size2) {
  36. mergedArr[k++] = arr2[j++];
  37. }
  38. }
  39. // 打印数组的函数
  40. void printArray(int arr[], int size) {
  41. for (int i = 0; i < size; i++) {
  42. printf("%d ", arr[i]);
  43. }
  44. printf("\n");
  45. }

算法2. 7 (略)

2. 3 线性表的链式表示和实现

2.3.1 线性链表

带头链表的单链表的实现

算法2.8(插入第i个节点) | 算法2.9(删除第i个节点) |算法2.10(头插法)|算法2.11(尾插发)

实现一个带头结点的单链表通常涉及以下几个步骤:

  1. 定义节点结构体;
  2. 初始化链表;
  3. 插入节点(头插法、尾插法);
  4. 删除节点(删除头结点后的第一个节点、删除指定节点);
  5. 遍历和显示链表;
  6. 销毁链表。

带头结点的单链表实现的例子:

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 定义节点结构体
  4. typedef struct Node {
  5. int data;
  6. struct Node *next;
  7. } Node;
  8. // 定义链表类型
  9. typedef struct LinkList {
  10. Node *head; // 指向链表的头结点
  11. } LinkList;
  12. // 初始化链表
  13. void InitList(LinkList *L) {
  14. L->head = (Node *)malloc(sizeof(Node));
  15. if (!L->head) {
  16. printf("Memory allocation failed.\n");
  17. exit(EXIT_FAILURE);
  18. }
  19. L->head->next = NULL; // 头结点的next置空
  20. }
  21. // 头插法插入节点
  22. void InsertToHead(LinkList *L, int data) {
  23. Node *new_node = (Node *)malloc(sizeof(Node));
  24. if (!new_node) {
  25. printf("Memory allocation failed.\n");
  26. exit(EXIT_FAILURE);
  27. }
  28. new_node->data = data;
  29. new_node->next = L->head->next;
  30. L->head->next = new_node;
  31. }
  32. // 尾插法插入节点
  33. void InsertToTail(LinkList *L, int data) {
  34. Node *new_node = (Node *)malloc(sizeof(Node));
  35. if (!new_node) {
  36. printf("Memory allocation failed.\n");
  37. exit(EXIT_FAILURE);
  38. }
  39. new_node->data = data;
  40. new_node->next = NULL;
  41. Node *p = L->head;
  42. while (p->next != NULL) {
  43. p = p->next;
  44. }
  45. p->next = new_node;
  46. }
  47. // 添加到第i个位置
  48. void InsertToIndex(LinkList *L, int index, int data) {
  49. if (index <= 0) {
  50. printf("Invalid index.\n");
  51. return;
  52. }
  53. Node *new_node = (Node *)malloc(sizeof(Node));
  54. if (!new_node) {
  55. printf("Memory allocation failed.\n");
  56. exit(EXIT_FAILURE);
  57. }
  58. new_node->data = data;
  59. Node *p = L->head;
  60. for (int i = 0; i < index - 1 && p->next != NULL; i++) {
  61. p = p->next;
  62. }
  63. if (p->next == NULL && index > 1) {
  64. printf("Index out of range.\n");
  65. free(new_node);
  66. return;
  67. }
  68. new_node->next = p->next;
  69. p->next = new_node;
  70. }
  71. //删除第i个位置
  72. void DeleteFromIndex(LinkList *L, int index) {
  73. if (index <= 0) {
  74. printf("Invalid index.\n");
  75. return;
  76. }
  77. Node *p = L->head;
  78. for (int i = 0; i < index - 1 && p->next != NULL; i++) {
  79. p = p->next;
  80. }
  81. if (p->next == NULL) {
  82. printf("Index out of range.\n");
  83. return;
  84. }
  85. Node *del_node = p->next;
  86. p->next = del_node->next;
  87. free(del_node);
  88. }
  89. // 打印链表
  90. void PrintList(LinkList *L) {
  91. Node *p = L->head->next;
  92. while (p != NULL) {
  93. printf("%d -> ", p->data);
  94. p = p->next;
  95. }
  96. printf("NULL\n");
  97. }
  98. // 销毁链表
  99. void DestroyList(LinkList *L) {
  100. Node *p = L->head;
  101. while (p != NULL) {
  102. Node *temp = p;
  103. p = p->next;
  104. free(temp);
  105. }
  106. L->head = NULL;
  107. }
  108. int main() {
  109. LinkList L;
  110. InitList(&L);
  111. // 插入一些节点
  112. InsertToHead(&L, 3);
  113. InsertToHead(&L, 2);
  114. InsertToHead(&L, 1);
  115. InsertToTail(&L, 4);
  116. InsertToTail(&L, 5);
  117. // 打印链表
  118. PrintList(&L);
  119. // 销毁链表
  120. DestroyList(&L);
  121. return 0;
  122. }

算法 2.12 (静态链表实现)

  1. #include <stdio.h>
  2. #define MAX_SIZE 100 // 定义静态链表的最大长度
  3. typedef struct {
  4. int data; // 数据部分
  5. int next; // 下一个节点的索引,如果是最后一个节点,则设置为 -1
  6. } Node;
  7. Node list[MAX_SIZE]; // 静态链表数组
  8. int head = -1; // 链表头的索引,初始为 -1 表示链表为空
  9. int free = 0; // 第一个可用位置的索引,即自由链表的头
  10. // 初始化静态链表
  11. void initStaticList() {
  12. for (int i = 0; i < MAX_SIZE - 1; i++) {
  13. list[i].next = i + 1;
  14. }
  15. list[MAX_SIZE - 1].next = -1; // 最后一个元素的 next 设置为 -1
  16. head = -1;
  17. free = 0;
  18. }
  19. // 插入节点到链表头部
  20. void insertAtHead(int data) {
  21. if (free == -1) {
  22. printf("No space left in the list.\n");
  23. return;
  24. }
  25. int newNode = free; // 获取新的节点索引
  26. free = list[free].next; // 更新自由链表的头
  27. list[newNode].data = data; // 设置新节点的数据
  28. list[newNode].next = head; // 新节点的 next 指向原链表头
  29. head = newNode; // 更新链表头
  30. }
  31. // 遍历并打印链表
  32. void printList() {
  33. int p = head;
  34. while (p != -1) {
  35. printf("%d -> ", list[p].data);
  36. p = list[p].next;
  37. }
  38. printf("NULL\n");
  39. }
  40. int main() {
  41. initStaticList(); // 初始化静态链表
  42. // 插入一些数据
  43. insertAtHead(3);
  44. insertAtHead(2);
  45. insertAtHead(1);
  46. insertAtHead(4);
  47. // 打印链表
  48. printList();
  49. return 0;
  50. }

算法 2.14:判断单链表是否为空 |2.15:清空单链表|2.16:求单链表的表长|2.17:在单链表的第 i 个位置插入元素 e

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 链表节点结构体
  4. typedef struct LNode {
  5. int data;
  6. struct LNode *next;
  7. } LNode, *LinkList;
  8. // 算法 2.13:初始化一个空的单链表
  9. void InitList(LinkList *L) {
  10. *L = (LinkList)malloc(sizeof(LNode));
  11. if (*L == NULL) {
  12. printf("内存分配失败\n");
  13. return;
  14. }
  15. (*L)->next = NULL;
  16. }
  17. // 算法 2.14:判断单链表是否为空
  18. int ListEmpty(LinkList L) {
  19. return L->next == NULL;
  20. }
  21. // 算法 2.15:清空单链表
  22. void ClearList(LinkList *L) {
  23. LinkList p, q;
  24. p = (*L)->next;
  25. while (p!= NULL) {
  26. q = p->next;
  27. free(p);
  28. p = q;
  29. }
  30. (*L)->next = NULL;
  31. }
  32. // 算法 2.16:求单链表的表长
  33. int ListLength(LinkList L) {
  34. int count = 0;
  35. LinkList p = L->next;
  36. while (p!= NULL) {
  37. count++;
  38. p = p->next;
  39. }
  40. return count;
  41. }
  42. // 算法 2.17:在单链表的第 i 个位置插入元素 e
  43. int ListInsert(LinkList L, int i, int e) {
  44. int j = 0;
  45. LinkList p = L, s;
  46. while (p!= NULL && j < i - 1) {
  47. p = p->next;
  48. j++;
  49. }
  50. if (p == NULL || j > i - 1) {
  51. printf("插入位置不合法\n");
  52. return 0;
  53. }
  54. s = (LinkList)malloc(sizeof(LNode));
  55. if (s == NULL) {
  56. printf("内存分配失败\n");
  57. return 0;
  58. }
  59. s->data = e;
  60. s->next = p->next;
  61. p->next = s;
  62. return 1;
  63. }
  64. int main() {
  65. LinkList L;
  66. // 初始化单链表
  67. InitList(&L);
  68. // 判断单链表是否为空
  69. if (ListEmpty(L)) {
  70. printf("single linkList is empty\n");
  71. } else {
  72. printf("single linkList is not empty\n");
  73. }
  74. // 插入元素
  75. ListInsert(L, 1, 10);
  76. ListInsert(L, 2, 20);
  77. ListInsert(L, 3, 30);
  78. // 求单链表的表长
  79. int length = ListLength(L);
  80. printf("single linkList length is %d\n", length);
  81. // 清空单链表
  82. ClearList(&L);
  83. return 0;
  84. }

2. 3. 2 循环链表

循环链表(Circular Linked List)

循环链表是一种特殊的链表,它的特点是链表中最后一个节点的指针不是指向空,而是指向链表的头节点,从而使整个链表形成一个环。

优点

  1. 从循环链表中的任何一个节点出发,都可以找到链表中的其他节点,这使得一些操作(如遍历整个链表)更加方便。
  2. 在某些应用中,如约瑟夫问题等,循环链表比普通链表更适合。

实现
循环链表的实现与普通链表类似,只是在创建链表和一些操作上需要注意形成循环。

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 链表节点结构体
  4. typedef struct ListNode {
  5. int data;
  6. struct ListNode *next;
  7. } ListNode;
  8. // 创建循环链表节点
  9. ListNode* createNode(int data) {
  10. ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
  11. if (newNode == NULL) {
  12. printf("Memory allocation failed\n");
  13. return NULL;
  14. }
  15. newNode->data = data;
  16. newNode->next = NULL;
  17. return newNode;
  18. }
  19. // 插入节点到循环链表头部
  20. void insertAtHead(ListNode** head, int data) {
  21. ListNode* newNode = createNode(data);
  22. if (*head == NULL) {
  23. // 如果链表为空,新节点就是唯一的节点,形成循环
  24. newNode->next = newNode;
  25. *head = newNode;
  26. } else {
  27. ListNode* temp = *head;
  28. while (temp->next!= *head) {
  29. temp = temp->next;
  30. }
  31. // 将新节点插入到头部,并更新循环
  32. newNode->next = *head;
  33. temp->next = newNode;
  34. *head = newNode;
  35. }
  36. }
  37. // 打印循环链表
  38. void printList(ListNode* head) {
  39. if (head == NULL) {
  40. printf("The linked list is empty\n");
  41. return;
  42. }
  43. ListNode* temp = head;
  44. do {
  45. printf("%d ", temp->data);
  46. temp = temp->next;
  47. } while (temp!= head);
  48. printf("\n");
  49. }
  50. // 释放循环链表内存
  51. void freeList(ListNode* head) {
  52. if (head == NULL) {
  53. return;
  54. }
  55. ListNode* curr = head;
  56. ListNode* next;
  57. do {
  58. next = curr->next;
  59. free(curr);
  60. curr = next;
  61. } while (curr!= head);
  62. }
  63. int main() {
  64. ListNode* head = NULL;
  65. insertAtHead(&head, 1);
  66. insertAtHead(&head, 2);
  67. insertAtHead(&head, 3);
  68. printf("Circular Linked List: ");
  69. printList(head);
  70. freeList(head);
  71. return 0;
  72. }
  • createNode 函数用于创建一个新的链表节点,并初始化其数据和指针。
  • insertAtHead 函数用于将一个新节点插入到循环链表的头部。如果链表为空,新节点自己形成一个循环;否则,将新节点插入到头部,并更新循环关系。
  • printList 函数用于打印循环链表的内容。通过一个 do-while 循环来实现,确保至少打印一次(因为循环链表的头部是确定的,即使链表只有一个节点也能正确打印)。
  • freeList 函数用于释放循环链表的内存,通过一个 do-while 循环依次释放每个节点的内存。

2. 3. 2 双向链表

双向链表定义

双向链表是一种链式存储结构,其中每个节点包含三个部分:

  1. 数据域:用于存储实际的数据信息。
  2. 前驱指针(Prev):指向其前一个节点的指针。
  3. 后继指针(Next):指向其后一个节点的指针。

这种结构使得双向链表上的节点可以向前和向后移动,提供了比单链表更灵活的访问能力。

特点

  • 双向性:由于每个节点都保存了前后节点的信息,所以在双向链表中可以很容易地从前向后或从后向前遍历链表。
  • 插入和删除操作:插入或删除一个节点时,需要同时更新被操作节点的前驱和后继节点的指针,因此相比单链表,双向链表的这些操作稍微复杂一些,但并不增加算法的时间复杂度。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 双向链表节点结构体
  4. typedef struct DuLNode {
  5. int data;
  6. struct DuLNode *prior;
  7. struct DuLNode *next;
  8. } DuLNode, *DuLinkList;
  9. // 创建双向链表节点
  10. DuLNode* createDuLNode(int data) {
  11. DuLNode* newNode = (DuLNode*)malloc(sizeof(DuLNode));
  12. if (newNode == NULL) {
  13. printf("Memory allocation failed\n");
  14. return NULL;
  15. }
  16. newNode->data = data;
  17. newNode->prior = NULL;
  18. newNode->next = NULL;
  19. return newNode;
  20. }
  21. // 在双向链表头部插入节点
  22. void insertAtHead(DuLinkList *L, int data) {
  23. DuLNode* newNode = createDuLNode(data);
  24. if (*L == NULL) {
  25. // 如果链表为空,新节点即为链表的唯一节点
  26. *L = newNode;
  27. newNode->prior = newNode;
  28. newNode->next = newNode;
  29. } else {
  30. // 将新节点插入到头部
  31. newNode->next = *L;
  32. newNode->prior = (*L)->prior;
  33. (*L)->prior->next = newNode;
  34. (*L)->prior = newNode;
  35. *L = newNode;
  36. }
  37. }
  38. // 打印双向链表
  39. void printDuLinkList(DuLinkList L) {
  40. if (L == NULL) {
  41. printf("The doubly linked list is empty\n");
  42. return;
  43. }
  44. DuLNode* curr = L;
  45. printf("Doubly Linked List: ");
  46. do {
  47. printf("%d ", curr->data);
  48. curr = curr->next;
  49. } while (curr!= L);
  50. printf("\n");
  51. }
  52. // 释放双向链表内存
  53. void freeDuLinkList(DuLinkList L) {
  54. if (L == NULL) {
  55. return;
  56. }
  57. DuLNode* curr = L;
  58. DuLNode* temp;
  59. do {
  60. temp = curr;
  61. curr = curr->next;
  62. free(temp);
  63. } while (curr!= L);
  64. }
  65. int main() {
  66. DuLinkList L = NULL;
  67. insertAtHead(&L, 10);
  68. insertAtHead(&L, 20);
  69. insertAtHead(&L, 30);
  70. printDuLinkList(L);
  71. freeDuLinkList(L);
  72. return 0;
  73. }
  • createDuLNode 函数用于创建一个新的双向链表节点,并进行内存分配和数据初始化。
  • insertAtHead 函数用于在双向链表的头部插入一个新节点。如果链表为空,新节点成为唯一节点,并建立双向循环链接;否则,将新节点插入到头部,并更新前后节点的链接关系。
  • printDuLinkList 函数用于打印双向链表的内容。如果链表为空,输出相应提示信息;否则,从头部开始遍历链表并打印每个节点的数据。
  • freeDuLinkList 函数用于释放双向链表的内存空间。通过遍历链表,依次释放每个节点的内存。

算法 2.18:取第 i 个元素|算法 2.19:在第 i 个位置插入元素 e|算法 2.20:删除第 i 个元素,并将其值赋给 e|算法 2.21:合并两个有序链表 La 和 Lb 为一个新的有序链表 Lc

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 链表节点结构体
  4. typedef struct LNode {
  5. int data;
  6. struct LNode *next;
  7. } LNode, *LinkList;
  8. // 算法 2.18:取第 i 个元素
  9. int GetElem(LinkList L, int i, int *e) {
  10. int j = 1;
  11. LinkList p = L->next;
  12. while (p!= NULL && j < i) {
  13. p = p->next;
  14. j++;
  15. }
  16. if (p == NULL || j > i) {
  17. return 0;
  18. }
  19. *e = p->data;
  20. return 1;
  21. }
  22. // 算法 2.19:在第 i 个位置插入元素 e
  23. int ListInsert(LinkList *L, int i, int e) {
  24. int j = 1;
  25. LinkList p = *L, s;
  26. while (p!= NULL && j < i - 1) {
  27. p = p->next;
  28. j++;
  29. }
  30. if (p == NULL || j > i - 1) {
  31. return 0;
  32. }
  33. s = (LinkList)malloc(sizeof(LNode));
  34. if (s == NULL) {
  35. return 0;
  36. }
  37. s->data = e;
  38. s->next = p->next;
  39. p->next = s;
  40. return 1;
  41. }
  42. // 算法 2.20:删除第 i 个元素,并将其值赋给 e
  43. int ListDelete(LinkList *L, int i, int *e) {
  44. int j = 1;
  45. LinkList p = *L, q;
  46. while (p->next!= NULL && j < i) {
  47. p = p->next;
  48. j++;
  49. }
  50. if (p->next == NULL || j > i) {
  51. return 0;
  52. }
  53. q = p->next;
  54. p->next = q->next;
  55. *e = q->data;
  56. free(q);
  57. return 1;
  58. }
  59. // 算法 2.21:合并两个有序链表 La 和 Lb 为一个新的有序链表 Lc
  60. LinkList MergeList(LinkList La, LinkList Lb) {
  61. LinkList Lc, pa, pb, pc;
  62. Lc = (LinkList)malloc(sizeof(LNode));
  63. pc = Lc;
  64. pa = La->next;
  65. pb = Lb->next;
  66. while (pa!= NULL && pb!= NULL) {
  67. if (pa->data <= pb->data) {
  68. pc->next = pa;
  69. pc = pa;
  70. pa = pa->next;
  71. } else {
  72. pc->next = pb;
  73. pc = pb;
  74. pb = pb->next;
  75. }
  76. }
  77. if (pa!= NULL) {
  78. pc->next = pa;
  79. }
  80. if (pb!= NULL) {
  81. pc->next = pb;
  82. }
  83. free(La);
  84. free(Lb);
  85. return Lc;
  86. }
  87. // 打印链表函数
  88. void PrintList(LinkList L) {
  89. LinkList p = L->next;
  90. while (p!= NULL) {
  91. printf("%d ", p->data);
  92. p = p->next;
  93. }
  94. printf("\n");
  95. }
  96. int main() {
  97. // 构建测试链表 La
  98. LinkList La = (LinkList)malloc(sizeof(LNode));
  99. La->next = NULL;
  100. ListInsert(&La, 1, 1);
  101. ListInsert(&La, 2, 3);
  102. ListInsert(&La, 3, 5);
  103. // 构建测试链表 Lb
  104. LinkList Lb = (LinkList)malloc(sizeof(LNode));
  105. Lb->next = NULL;
  106. ListInsert(&Lb, 1, 2);
  107. ListInsert(&Lb, 2, 4);
  108. ListInsert(&Lb, 3, 6);
  109. printf("链表 La: ");
  110. PrintList(La);
  111. printf("链表 Lb: ");
  112. PrintList(Lb);
  113. // 测试算法 2.18:取第 2 个元素
  114. int e;
  115. if (GetElem(La, 2, &e)) {
  116. printf("链表 La 中第 2 个元素为: %d\n", e);
  117. } else {
  118. printf("获取元素失败\n");
  119. }
  120. // 测试算法 2.19:在第 4 个位置插入元素 7
  121. if (ListInsert(&La, 4, 7)) {
  122. printf("在链表 La 中第 4 个位置插入元素 7 成功\n");
  123. } else {
  124. printf("插入元素失败\n");
  125. }
  126. printf("插入元素后的链表 La: ");
  127. PrintList(La);
  128. // 测试算法 2.20:删除第 3 个元素
  129. if (ListDelete(&La, 3, &e)) {
  130. printf("从链表 La 中删除第 3 个元素 %d 成功\n", e);
  131. } else {
  132. printf("删除元素失败\n");
  133. }
  134. printf("删除元素后的链表 La: ");
  135. PrintList(La);
  136. // 测试算法 2.21:合并链表 La 和 Lb 为 Lc
  137. LinkList Lc = MergeList(La, Lb);
  138. printf("合并后的链表 Lc: ");
  139. PrintList(Lc);
  140. return 0;
  141. }

2.4 一元多项式的表示及相加

一元多项式的表示

在数学中,一元多项式可以表示为:

\rho _{_{n}}(x) = a_{n}x^{n}+a_{n-1}x^{n-1}+....+a_{1}x+a_{0}

在计算机中,为了有效地表示和操作一元多项式,可以使用链表来实现。每个链表节点表示多项式的一项,包含系数和指数两个数据域。

多项式的相加

多项式相加的基本思想是:将两个多项式的对应项进行系数相加,如果某一项的指数在另一个多项式中不存在,则将其直接添加到结果多项式中。

具体步骤如下:

  1. 同时遍历两个待相加的多项式链表。
  2. 比较当前两个节点的指数:
    • 如果指数相等,则将系数相加,创建一个新的节点并插入到结果多项式链表中,然后同时向前移动两个多项式的指针。
    • 如果一个多项式的当前节点指数小于另一个多项式的当前节点指数,则将指数较小的节点插入到结果多项式链表中,并向前移动该多项式的指针。
    • 如果一个多项式遍历完,而另一个多项式还有剩余节点,则将剩余节点依次插入到结果多项式链表中。
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 多项式项的结构体
  4. typedef struct PolyNode {
  5. int coef; // 系数
  6. int expo; // 指数
  7. struct PolyNode *next;
  8. } PolyNode, *Polynomial;
  9. // 创建新的多项式项节点
  10. PolyNode *createNode(int coef, int expo) {
  11. PolyNode *newNode = (PolyNode *)malloc(sizeof(PolyNode));
  12. if (newNode == NULL) {
  13. printf("内存分配失败\n");
  14. return NULL;
  15. }
  16. newNode->coef = coef;
  17. newNode->expo = expo;
  18. newNode->next = NULL;
  19. return newNode;
  20. }
  21. // 插入多项式项到链表中(按照指数降序排列)
  22. void insertTerm(Polynomial *poly, int coef, int expo) {
  23. PolyNode *newNode = createNode(coef, expo);
  24. if (*poly == NULL) {
  25. // 如果链表为空,直接将新节点作为链表的头节点
  26. *poly = newNode;
  27. } else {
  28. PolyNode *curr = *poly;
  29. PolyNode *prev = NULL;
  30. while (curr!= NULL && curr->expo > expo) {
  31. prev = curr;
  32. curr = curr->next;
  33. }
  34. if (curr!= NULL && curr->expo == expo) {
  35. // 如果指数相同,系数相加
  36. curr->coef += coef;
  37. free(newNode);
  38. if (curr->coef == 0) {
  39. // 如果相加后系数为 0,删除该节点
  40. if (prev == NULL) {
  41. *poly = curr->next;
  42. } else {
  43. prev->next = curr->next;
  44. }
  45. free(curr);
  46. }
  47. } else {
  48. // 插入新节点
  49. if (prev == NULL) {
  50. newNode->next = *poly;
  51. *poly = newNode;
  52. } else {
  53. newNode->next = curr;
  54. prev->next = newNode;
  55. }
  56. }
  57. }
  58. }
  59. // 打印多项式
  60. void printPolynomial(Polynomial poly) {
  61. PolyNode *curr = poly;
  62. while (curr!= NULL) {
  63. printf("%dx^%d", curr->coef, curr->expo);
  64. curr = curr->next;
  65. if (curr!= NULL) {
  66. printf(" + ");
  67. }
  68. }
  69. printf("\n");
  70. }
  71. // 多项式相加
  72. Polynomial addPolynomials(Polynomial poly1, Polynomial poly2) {
  73. Polynomial result = NULL;
  74. PolyNode *p1 = poly1;
  75. PolyNode *p2 = poly2;
  76. while (p1!= NULL && p2!= NULL) {
  77. if (p1->expo > p2->expo) {
  78. insertTerm(&result, p1->coef, p1->expo);
  79. p1 = p1->next;
  80. } else if (p1->expo < p2->expo) {
  81. insertTerm(&result, p2->coef, p2->expo);
  82. p2 = p2->next;
  83. } else {
  84. int coefSum = p1->coef + p2->coef;
  85. insertTerm(&result, coefSum, p1->expo);
  86. p1 = p1->next;
  87. p2 = p2->next;
  88. }
  89. }
  90. while (p1!= NULL) {
  91. insertTerm(&result, p1->coef, p1->expo);
  92. p1 = p1->next;
  93. }
  94. while (p2!= NULL) {
  95. insertTerm(&result, p2->coef, p2->expo);
  96. p2 = p2->next;
  97. }
  98. return result;
  99. }
  100. // 释放多项式链表的内存
  101. void freePolynomial(Polynomial poly) {
  102. PolyNode *curr = poly;
  103. PolyNode *next;
  104. while (curr!= NULL) {
  105. next = curr->next;
  106. free(curr);
  107. curr = next;
  108. }
  109. }
  110. int main() {
  111. Polynomial poly1 = NULL;
  112. Polynomial poly2 = NULL;
  113. // 构建第一个多项式:3x^4 + 2x^2 + 1
  114. insertTerm(&poly1, 3, 4);
  115. insertTerm(&poly1, 2, 2);
  116. insertTerm(&poly1, 1, 0);
  117. // 构建第二个多项式:2x^3 - 3x^2 + 5
  118. insertTerm(&poly2, 2, 3);
  119. insertTerm(&poly2, -3, 2);
  120. insertTerm(&poly2, 5, 0);
  121. printf("多项式 1: ");
  122. printPolynomial(poly1);
  123. printf("多项式 2: ");
  124. printPolynomial(poly2);
  125. Polynomial sum = addPolynomials(poly1, poly2);
  126. printf("相加后的结果: ");
  127. printPolynomial(sum);
  128. // 释放内存
  129. freePolynomial(poly1);
  130. freePolynomial(poly2);
  131. freePolynomial(sum);
  132. return 0;
  133. }

算法2.22

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 定义多项式的项的结构体
  4. typedef struct PolynomialNode {
  5. int coef; // 系数
  6. int expo; // 指数
  7. struct PolynomialNode *next;
  8. } PolyNode, *Polynomial;
  9. // 获取链表头节点
  10. Polynomial GetHead(Polynomial P) {
  11. return P;
  12. }
  13. // 获取下一个节点
  14. Polynomial NextPos(Polynomial P, Polynomial pos) {
  15. return pos->next;
  16. }
  17. // 获取当前节点的元素值
  18. void GetCurElem(Polynomial pos, int *coef, int *expo) {
  19. *coef = pos->coef;
  20. *expo = pos->expo;
  21. }
  22. // 比较两个整数
  23. int cmp(int a, int b) {
  24. if (a < b) {
  25. return -1;
  26. } else if (a == b) {
  27. return 0;
  28. } else {
  29. return 1;
  30. }
  31. }
  32. // 设置当前节点的元素值
  33. void SetCurElem(Polynomial pos, int coef) {
  34. pos->coef = coef;
  35. }
  36. // 删除链表中的第一个节点
  37. void DelFirst(Polynomial head, Polynomial *pos) {
  38. Polynomial temp = *pos;
  39. *pos = (*pos)->next;
  40. free(temp);
  41. }
  42. // 在指定位置插入节点
  43. void InsFirst(Polynomial head, Polynomial node) {
  44. node->next = head->next;
  45. head->next = node;
  46. }
  47. // 判断链表是否为空
  48. int ListEmpty(Polynomial P) {
  49. return P->next == NULL;
  50. }
  51. // 将一个链表追加到另一个链表的末尾
  52. void Append(Polynomial Pa, Polynomial Pb) {
  53. Polynomial p = Pa;
  54. while (p->next!= NULL) {
  55. p = p->next;
  56. }
  57. p->next = Pb->next;
  58. Pb->next = NULL;
  59. }
  60. // 释放节点内存
  61. void FreeNode(Polynomial node) {
  62. free(node);
  63. }
  64. // 多项式加法函数
  65. void AddPolyn(Polynomial *Pa, Polynomial *Pb) {
  66. Polynomial ha = GetHead(*Pa), hb = GetHead(*Pb);
  67. Polynomial qa = NextPos(*Pa, ha), qb = NextPos(*Pb, hb);
  68. while (qa && qb) {
  69. int a_coef, a_expo, b_coef, b_expo;
  70. GetCurElem(qa, &a_coef, &a_expo);
  71. GetCurElem(qb, &b_coef, &b_expo);
  72. switch (cmp(a_expo, b_expo)) {
  73. case -1:
  74. ha = qa;
  75. qa = NextPos(*Pa, qa);
  76. break;
  77. case 0:
  78. int sum = a_coef + b_coef;
  79. if (sum!= 0) {
  80. SetCurElem(qa, sum);
  81. ha = qa;
  82. } else {
  83. DelFirst(ha, &qa);
  84. FreeNode(qa);
  85. }
  86. DelFirst(hb, &qb);
  87. FreeNode(qb);
  88. qa = NextPos(*Pa, ha);
  89. qb = NextPos(*Pb, hb);
  90. break;
  91. case 1:
  92. DelFirst(hb, &qb);
  93. InsFirst(ha, qb);
  94. qb = NextPos(*Pb, hb);
  95. ha = NextPos(*Pa, ha);
  96. break;
  97. }
  98. }
  99. if (!ListEmpty(*Pb)) {
  100. Append(*Pa, qb);
  101. }
  102. FreeNode(hb);
  103. }
  104. // 打印多项式函数
  105. void PrintPolynomial(Polynomial P) {
  106. Polynomial p = P->next;
  107. while (p) {
  108. printf("%dx^%d", p->coef, p->expo);
  109. if (p->next) {
  110. printf(" + ");
  111. }
  112. p = p->next;
  113. }
  114. printf("\n");
  115. }
  116. // 创建多项式函数
  117. Polynomial CreatePolynomial() {
  118. Polynomial head = (Polynomial)malloc(sizeof(PolyNode));
  119. head->next = NULL;
  120. return head;
  121. }
  122. // 向多项式中添加项函数
  123. void AddTerm(Polynomial P, int coef, int expo) {
  124. Polynomial newNode = (Polynomial)malloc(sizeof(PolyNode));
  125. newNode->coef = coef;
  126. newNode->expo = expo;
  127. newNode->next = NULL;
  128. Polynomial p = P;
  129. while (p->next && p->next->expo > expo) {
  130. p = p->next;
  131. }
  132. if (p->next && p->next->expo == expo) {
  133. p->next->coef += coef;
  134. if (p->next->coef == 0) {
  135. Polynomial temp = p->next;
  136. p->next = p->next->next;
  137. free(temp);
  138. }
  139. } else {
  140. newNode->next = p->next;
  141. p->next = newNode;
  142. }
  143. }
  144. int main() {
  145. Polynomial Pa = CreatePolynomial();
  146. Polynomial Pb = CreatePolynomial();
  147. // 向多项式 Pa 中添加项
  148. AddTerm(Pa, 3, 2);
  149. AddTerm(Pa, 2, 1);
  150. AddTerm(Pa, 1, 0);
  151. // 向多项式 Pb 中添加项
  152. AddTerm(Pb, 4, 2);
  153. AddTerm(Pb, 3, 1);
  154. AddTerm(Pb, 2, 0);
  155. printf("多项式 Pa: ");
  156. PrintPolynomial(Pa);
  157. printf("多项式 Pb: ");
  158. PrintPolynomial(Pb);
  159. AddPolyn(&Pa, &Pb);
  160. printf("多项式 Pa + Pb: ");
  161. PrintPolynomial(Pa);
  162. // 释放内存
  163. Polynomial temp;
  164. while (Pa) {
  165. temp = Pa;
  166. Pa = Pa->next;
  167. free(temp);
  168. }
  169. while (Pb) {
  170. temp = Pb;
  171. Pb = Pb->next;
  172. free(temp);
  173. }
  174. return 0;
  175. }

算法2.23

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. // 多项式项的结构体
  4. typedef struct PolyNode {
  5. int coef; // 系数
  6. int expo; // 指数
  7. struct PolyNode *next;
  8. } PolyNode, *Polynomial;
  9. // 创建新的多项式项节点
  10. PolyNode *createNode(int coef, int expo) {
  11. PolyNode *newNode = (PolyNode *)malloc(sizeof(PolyNode));
  12. if (newNode == NULL) {
  13. printf("内存分配失败\n");
  14. return NULL;
  15. }
  16. newNode->coef = coef;
  17. newNode->expo = expo;
  18. newNode->next = NULL;
  19. return newNode;
  20. }
  21. // 插入多项式项到链表中(按照指数降序排列)
  22. void insertTerm(Polynomial *poly, int coef, int expo) {
  23. PolyNode *newNode = createNode(coef, expo);
  24. if (*poly == NULL) {
  25. // 如果链表为空,直接将新节点作为链表的头节点
  26. *poly = newNode;
  27. } else {
  28. PolyNode *curr = *poly;
  29. PolyNode *prev = NULL;
  30. while (curr!= NULL && curr->expo > expo) {
  31. prev = curr;
  32. curr = curr->next;
  33. }
  34. if (curr!= NULL && curr->expo == expo) {
  35. // 如果指数相同,系数相加
  36. curr->coef += coef;
  37. free(newNode);
  38. if (curr->coef == 0) {
  39. // 如果相加后系数为 0,删除该节点
  40. if (prev == NULL) {
  41. *poly = curr->next;
  42. } else {
  43. prev->next = curr->next;
  44. }
  45. free(curr);
  46. }
  47. } else {
  48. // 插入新节点
  49. if (prev == NULL) {
  50. newNode->next = *poly;
  51. *poly = newNode;
  52. } else {
  53. newNode->next = curr;
  54. prev->next = newNode;
  55. }
  56. }
  57. }
  58. }
  59. // 多项式相加函数
  60. Polynomial addPolynomials(Polynomial poly1, Polynomial poly2) {
  61. Polynomial result = NULL;
  62. PolyNode *p1 = poly1;
  63. PolyNode *p2 = poly2;
  64. while (p1!= NULL && p2!= NULL) {
  65. if (p1->expo > p2->expo) {
  66. insertTerm(&result, p1->coef, p1->expo);
  67. p1 = p1->next;
  68. } else if (p1->expo < p2->expo) {
  69. insertTerm(&result, p2->coef, p2->expo);
  70. p2 = p2->next;
  71. } else {
  72. int coefSum = p1->coef + p2->coef;
  73. insertTerm(&result, coefSum, p1->expo);
  74. p1 = p1->next;
  75. p2 = p2->next;
  76. }
  77. }
  78. while (p1!= NULL) {
  79. insertTerm(&result, p1->coef, p1->expo);
  80. p1 = p1->next;
  81. }
  82. while (p2!= NULL) {
  83. insertTerm(&result, p2->coef, p2->expo);
  84. p2 = p2->next;
  85. }
  86. return result;
  87. }
  88. // 多项式相乘函数
  89. Polynomial multiplyPolynomials(Polynomial poly1, Polynomial poly2) {
  90. Polynomial result = NULL;
  91. PolyNode *p2 = poly2;
  92. while (p2!= NULL) {
  93. Polynomial temp = NULL;
  94. PolyNode *p1 = poly1;
  95. while (p1!= NULL) {
  96. int coef = p1->coef * p2->coef;
  97. int expo = p1->expo + p2->expo;
  98. insertTerm(&temp, coef, expo);
  99. p1 = p1->next;
  100. }
  101. result = addPolynomials(result, temp);
  102. p2 = p2->next;
  103. }
  104. return result;
  105. }
  106. // 打印多项式
  107. void printPolynomial(Polynomial poly) {
  108. PolyNode *curr = poly;
  109. while (curr!= NULL) {
  110. printf("%dx^%d", curr->coef, curr->expo);
  111. curr = curr->next;
  112. if (curr!= NULL) {
  113. printf(" + ");
  114. }
  115. }
  116. printf("\n");
  117. }
  118. // 释放多项式链表的内存
  119. void freePolynomial(Polynomial poly) {
  120. PolyNode *curr = poly;
  121. PolyNode *next;
  122. while (curr!= NULL) {
  123. next = curr->next;
  124. free(curr);
  125. curr = next;
  126. }
  127. }
  128. int main() {
  129. Polynomial poly1 = NULL;
  130. Polynomial poly2 = NULL;
  131. // 构建第一个多项式:3x^2 + 2x + 1
  132. insertTerm(&poly1, 3, 2);
  133. insertTerm(&poly1, 2, 1);
  134. insertTerm(&poly1, 1, 0);
  135. // 构建第二个多项式:2x^2 - 1
  136. insertTerm(&poly2, 2, 2);
  137. insertTerm(&poly2, -1, 0);
  138. printf("多项式 1: ");
  139. printPolynomial(poly1);
  140. printf("多项式 2: ");
  141. printPolynomial(poly2);
  142. Polynomial product = multiplyPolynomials(poly1, poly2);
  143. printf("相乘后的结果: ");
  144. printPolynomial(product);
  145. // 释放内存
  146. freePolynomial(poly1);
  147. freePolynomial(poly2);
  148. freePolynomial(product);
  149. return 0;
  150. }

总结

1.1. 线性表的概念

  • 线性表是n个相同类型数据元素的有限序列(n≥0)。
  • 具有如下特征:
    • 存在一个唯一的“第一个”数据元素。
    • 存在一个唯一的“最后一个”数据元素。
    • 除第一个之外,每个数据元素均有且仅有一个前驱。
    • 除最后一个之外,每个数据元素均有且仅有一个后继。

1.2. 线性表的分类

  • 线性表的抽象数据类型(ADT):定义了线性表的基本操作集。
  • 顺序表:线性表的顺序存储结构,利用一组地址连续的存储单元依次存放线性表中的各个元素。
  • 链表:线性表的链式存储结构,通过一组任意的存储单元来存储线性表中的数据元素,每个存储单元(节点)包括数据域和指针域。

1.3. 顺序表的特点

  • 随机存取:可以直接通过计算得到任一元素的地址。
  • 插入和删除操作需要移动元素,效率较低。
  • 需要预分配足够的存储空间。

1.4. 链表的特点

  • 动态存储分配:不需要预分配存储空间,可以动态申请和释放节点。
  • 插入和删除操作效率高,仅需修改指针。
  • 随机访问效率低,需要从头节点开始遍历。

1.5. 链表的类型

  • 单链表:每个节点仅包含一个指向其后继节点的指针。
  • 双链表:每个节点包含两个指针,分别指向其前驱和后继节点。
  • 循环链表:最后一个节点的指针指向头节点,形成环状结构。
  • 静态链表:使用静态数组实现的链表,利用数组下标作为指针。

1.6. 线性表的操作

  • 初始化:创建一个空的线性表。
  • 插入:在表中插入一个元素。
  • 删除:从表中删除一个元素。
  • 查找:在表中查找一个元素。
  • 遍历:访问表中的所有元素。
  • 求长度:获取表中元素的数量。

1.7. 时间复杂度分析

  • 顺序表中,插入和删除操作的时间复杂度为O(n),查找和求长度操作的时间复杂度为O(1)。
  • 链表中,插入和删除操作的时间复杂度为O(1)(在已知前驱节点的情况下),查找操作的时间复杂度为O(n)。

1.8. 空间复杂度分析

  • 顺序表的空间复杂度为O(n)。
  • 链表的空间复杂度也为O(n),但额外需要存储指针。

通过理解和掌握这些概念和操作,你可以有效地使用线性表解决实际问题,同时为学习更复杂的数据结构奠定坚实的基础。

拓展的学习内容

线性表

  1. 将两个或两个以上的线性表合并成一个线性表;
  2. 把一个线性表拆开成两个或两个以上的线性表;
  3. 重新复制一个线性表等;
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/820360
推荐阅读
相关标签
  

闽ICP备14008679号