今天我们来看看单链表排序中的冒泡排序,插入排序,选择排序。
文章目录
- 冒泡排序
- 交换值
- 交换节点
- 插入排序
- 交换节点
- 选择排序
- 交换值
- 交换节点
冒泡排序
交换值
首先我们来看看不交换节点,只进行值交换的形式,与数组下的实现思路一致。
外层循环通过 turn
指针遍历链表,每次迭代将当前节点作为的“轮次”节点,记录趟数,直到链表中最后一个节点(即 turn->next
为 NULL)。
内层循环通过 move
指针遍历链表,来进行两两比较。
void bubbleSort(ListNode* head) {if (head == NULL || head->next == NULL) {return;}for (ListNode* turn = head; turn->next != NULL; turn = turn->next) {for (ListNode* move = head; move->next != NULL; move = move->next) {if (move->val > move->next->val) {int temp = move->val;move->val = move->next->val;move->next->val = temp;}}}
}
我们还可以在创建一个save
指针,由于每一趟冒泡排序都会从后向前排序好一个元素,闹我们每一趟结束时都使用save
指针最后一个元素,并在下一趟不再遍历它,这样下一次内部循环就会排除已经排序好的节点。
void bubblesort(ListNode *head) {ListNode *turn = head;ListNode *move = head;ListNode *save = NULL;if (head == NULL || head->next == NULL) {return;}for (turn = head; turn->next != NULL; turn = turn->next) {for (move = head; move->next != save; move = move->next) {if (move->val > move->next->val) {int temp = move->val;move->val = move->next->val;move->next->val = temp;}}save = move;}
}
这是另外一种写法
swapped
:标志位,用于指示在当前遍历中是否发生了节点交换。
ptr1
:指向当前正在比较的节点的指针。
lptr
:用于标记每次遍历的最后一个未排序节点的指针。
具体思路是:
- 在每次循环中,遍历链表并比较相邻的节点的值。
- 如果当前节点的值大于下一个节点的值,则交换它们的值,并将
swapped
标志位置为 1,表示发生了交换。 - 遍历完成后,将
lptr
指向最后一个已排序节点。
void swap(ListNode* a, ListNode* b) {int temp = a->val;a->val = b->val;b->val = temp;
}
void bubbleSort(ListNode* start) {int swapped;ListNode* ptr1;ListNode* lptr = NULL;// 如果链表为空或只有一个节点,则无需排序if (start == NULL)return;do {swapped = 0;ptr1 = start;while (ptr1->next != lptr) {if (ptr1->val > ptr1->next->val) {swap(ptr1, ptr1->next);swapped = 1;}ptr1 = ptr1->next;}lptr = ptr1;} while (swapped);
}
交换节点
接下来看看交换节点而非交换值的排序方法
在外层循环中,条件是 head->next->next != tail
,这意味着只要链表中至少有两个未排序的节点,就会继续排序。内层循环通过 cur
和 prev
遍历链表,如果发现 cur->val > cur->next->val
(即当前节点的值大于下一个节点的值),则执行交换操作。
交换节点具体为:
- 将
prev
的next
指针指向cur
的下一个节点(即要交换的节点)。 - 将
cur
的next
指针指向cur
下一个节点的下一个节点。 - 将交换节点的
next
指针指向cur
,完成交换。
在内层循环的末尾,更新 cur
和 prev
指针,以便继续遍历。内层循环结束后,更新 tail
指针,使其指向已排序的链表尾部。
void BubbleSort(ListNode* head) {ListNode *tail = NULL, *prev, *cur;while ((head->next->next) != tail) {prev = head;cur = head->next;while ((cur->next) != tail) {if ((cur->val) > (cur->next->val)) {prev->next = cur->next;cur->next = cur->next->next;prev->next->next = cur;cur = prev->next;}cur = cur->next;prev = prev->next;}tail = cur; // 更新尾节点}
}
插入排序
交换节点
插入排序的核心思想是将待排序部分的每一个元素插入到已排序部分的合适位置。
具体步骤如下:
-
创建一个新的头节点
shead
,作为哨兵节点,可以简化插入操作。 -
lastSorted
指针指向当前已排序的最后一个节点,初始时是链表的第一个节点。 -
cur
指针指向lastSorted
的下一个节点,即尚未排序的节点。 -
在一个
while
循环中,代码遍历所有尚未排序的节点。- 如果当前节点的值
cur->val
大于或等于lastSorted
节点的值,说明当前节点在排序序列中的位置是正确的,只需要移动lastSorted
指针到下一个节点。 - 如果当前节点的值小于
lastSorted
节点的值,则需要将当前节点cur
插入到已排序序列的正确位置。
- 如果当前节点的值
-
插入操作是通过从哨兵节点
shead
开始,查找直到找到一个节点pre
,使得pre->next->val
大于或等于cur->val
。这表明cur
应该插入到pre
和pre->next
之间。 -
插入操作完成后,
cur
指针被移动到lastSorted
的下一个节点,以便在下一轮循环中继续排序。
struct ListNode* insertionSortList(struct ListNode* head) {if (head == NULL) {return head;}// 创建一个新的头节点 shead 作为辅助节点struct ListNode* shead = (struct ListNode*)malloc(sizeof(struct ListNode));shead->next = head;shead->val = 0;//定义两个指针,lastSorted 指向已排序部分的最后一个节点,cur 指向当前待插入节点的下一个节点struct ListNode* lastSorted = head;struct ListNode* cur = head->next;while (cur) {//如果当前节点的值大于等于已排序部分的最后一个节点的值,则直接将 lastSorted 向后移动一位if (cur->val >= lastSorted->val) {lastSorted = lastSorted->next;} else {// 否则,从头节点开始遍历链表,找到第一个比当前节点值大的节点的前一个节点 prestruct ListNode* pre = shead;while (pre->next->val <= cur->val) {pre = pre->next;}// 将当前节点插入到 pre 后面lastSorted->next = cur->next;cur->next = pre->next;pre->next = cur;}// 更新当前待插入节点为 lastSorted 的下一个节点cur = lastSorted->next;}return shead->next;
}
选择排序
选择排序的基本思想:
每一轮选取未排定的部分中最小的元素交换到未排定部分的最开头,经过若干个步骤,就能排定整个数组。
交换值
min_node
用来记录遍历过程中找到的最小值节点。另一个指针ptr2
从ptr1
的下一个节点开始遍历剩余的链表,找到未排序部分的最小节点。- 在
ptr2
的遍历过程中,如果发现一个节点的值比min_node
的值小,则更新min_node
。 - 当
ptr2
遍历完未排序部分后,检查min_node
是否就是ptr1
。如果不是,交换ptr1
和min_node
的值。 - 最后,
ptr1
向前移动一个节点,对下一对节点进行同样的操作。
struct ListNode* sectionSort(struct ListNode* head) {struct ListNode* ptr1 = head;// 当前未排序链表的第一个节点while (ptr1 && ptr1->next) {// 未排序链表中的最小值节点struct ListNode* min_node = ptr1;struct ListNode* ptr2 = ptr1->next;// 找到未排序链表中的最小值节点while (ptr2) {if (ptr2->val < min_node->val) {min_node = ptr2;}ptr2 = ptr2->next;}// 交换最小值节点与未排序链表中第一个节点的值if (ptr1 != min_node) {int temp = ptr1->val;ptr1->val = min_node->val;min_node->val = temp;}ptr1 = ptr1->next;}return head;
}
交换节点
定义了几个指针tail
,prev
,cur
,max
,max_prev
,用于在链表中遍历和记录最大值节点及其前一个节点。
外层循环通过 tail
遍历链表的每个部分,直到链表尾部。内层循环通过 cur
和 prev
寻找从 tail->next
开始到链表末尾的最大节点 max
和它的前一个节点 max_prev
。如果找到的最大节点不是 tail
的下一个节点,说明最大节点不在正确位置,通过交换节点将其移动到 tail
之后。
void SelectSort(ListNode* head) {ListNode *tail, *prev, *cur, *max, *max_prev;for (tail = head; tail && tail->next; tail = tail->next) {max = tail->next;for (cur = tail->next, prev = tail; cur != NULL; prev = cur, cur = cur->next) {if (cur->val > max->val) {max = cur;max_prev = prev;}}if (tail->next != max) {max_prev->next = max->next;max->next = tail->next;tail->next = max;}}
}
如有错误烦请指正。
感谢您的阅读