左神算法基础class3—题目12单向链表按某值划分成左边小、中间相等、右边大的形式
普通版:单向链表按某值划分成左边小、中间相等、右边大的形式(不用考虑稳定性且额外空间复杂度为O(n))
【题目】 给定一个单向链表的头节点head,节点的值类型是整型,再给定一个整数num。实现一个调整链表的函数,将链表调整为左部分都是值小于 num的节点,中间部分都是值等于num的节点,右部分都是值大于num的节点。
例如:链表9->0->4->5->1,num=3。 调整后链表可以是1->0->4->9->5,也可以是0->1->9->5->4。总之,满足左部分都是小于3的节点,中间部分都是等于3的节点(本例中这个部分为空),右部分都是大于3的节点即可。对某部分内部的节点顺序不做要求。
1.分析
总体思路是:左边小、中间相等、右边大的形式立马想到荷兰国旗问题(请参考荷兰国旗问题详解),考虑先把链表中的元素放入数组中,再使用荷兰国旗的方法排好序,最后把数组中的元素拷贝回链表中。
2.核心代码
(1)链表的生成及添加元素
①链表结构体定义
数据域,指针域构成
typedef struct ListNode
{
int val;
ListNode *next;
}*List;
②生成头节点
注意头节点需指向空
List MakeEmpty()
{
List head = (List)malloc(sizeof(ListNode));
head->next = NULL;
return head;
}
③尾插法
void insert(List head, int num)
{
//生成新的节点
List p = (List)malloc(sizeof(ListNode));
p->val = num;
p->next = NULL;
//遍历找到链表最后的位置
List r = head;
while(r->next != NULL)
{
r = r->next;
}
//新节点加入链表,连在末尾处
r->next = p;
}
(2)把链表放入数组中
使用vector容器,通过push_back()函数放入数据
vector<int> list_to_arr(List head)
{
head = head->next;//跳过头节点从第一个数开始
vector<int> arr;
while(head!=NULL)
{
arr.push_back(head->val);
head = head->next;
}
return arr;
}
(3)荷兰国旗排序
①用变量x代表小于区域的位置、y代表大于num的区域。刚开始由于不存在小于和大于的区域,则x = -1,y = length,表示指向数组-1、length(都不存在)。再设置变量cur作为遍历的位置,刚开始cur = 0指向数组第一个数。
②.开始遍历
A.如果cur<num ,那么把cur当前的元素和小于区域的下一个数交换,即++x的位置和cur位置的数交换,cur,x后移;
B.如果cur>num,那么把cur当前的元素和大于区域的前一个数交换,即–-y的位置和cur位置的数交换,y前移,cur不变(因为交换过来的数不确定还要再判断)
C.如果cur == num,cur++。
D.如果cur == y结束。
void sort(vector<int> &arr,int num)
{
int cur = 0;
int x = -1;
int y = arr.size();
while(cur < y)
{
if(arr[cur] < num)
{
swap(arr[cur++],arr[++x]);
}
else if(arr[cur] == num)
{
cur++;
}
else
{
swap(arr[cur],arr[--y]);
}
}
}
(4)把数组放回链表中
使用vector容器的迭代器,遍历容器依次放入(或者使用for循环,这里只是想用下迭代器,好久没用了)
void arr_to_list(vector<int>arr