这次的题目是LeetCode刷题——单链表(详细图解)进阶版有兴趣的朋友可以看一看
温馨提示:
做数据结构题的时候一定要多画图,考虑到特殊情况。
1、复制带随机指针的链表
知道整体思路后敲出代码就不难了
整体思路
1️⃣创建n个拷贝空间,依次和原链表相连。
2️⃣给random赋地址。
3️⃣把拷贝链表和原链表断开,单独返回拷贝链表。
①创建结点
这里要注意判断原链表为空的情况
②链接结点
③random
先看第一个拷贝结点,从图中直接看他应该指向NULL,所以①的random指向NULL,这种情况应该特殊处理
再看第二个拷贝结点,通过原结点看②应该指向①,那么用代码如何表示呢?
假设val为13的结点的指针为p
那么①的结点就可以表示为 p->random->next
④链接拷贝结点后返回
/**
* Definition for a Node.
* struct Node {
* int val;
* struct Node *next;
* struct Node *random;
* };
*/
struct Node* copyRandomList(struct Node* head) {
if(head == NULL)
{
return NULL;
}
struct Node* cur = head;
//链接拷贝结点
while(cur)
{
struct Node* next = cur->next;
struct Node* tmp = (struct Node*)malloc(sizeof(struct Node));
tmp->val = cur->val;
cur->next = tmp;
tmp->next = next;
cur = next;
}
//random
cur = head;
while(cur)
{
struct Node* next = cur->next;
//要考虑NULL情况
if(cur->random == NULL)
{
next->random = NULL;
}
else
{
next->random = cur->random->next;
}
cur = next->next;
}
//链接拷贝结点
cur = head->next;
//只有一个结点
if(cur->next == NULL)
{
return cur;
}
struct Node* next = cur->next->next;
struct Node* tmp = cur;
while(next->next)
{
cur->next = next;
cur = next;
next = cur->next->next;
}
cur->next = next;
return tmp;
}
2、对链表进行插入排序
关于插入排序,如图所示:
知道流程以后就可以考虑大致框架和特殊情况思考
我们的大致框架是先把第一个结点当作已经排序好的头,然后把后面的结点依次插入排序。
写出插入排序不难,但是有些特殊情况要考虑到
1️⃣链表为空
2️⃣要插入的数字比头还要小
3️⃣要插入的元素是最大的数字(要排到最后)
①当链表为空,返回NULL。
②当要插入的数字比头还要小
插入完后
③要插入的元素是最大的数字(要排到最后)
如果发现是到最后都没有插进去,直接让最后一个元素指向要插入的元素,要插入的元素在指向NULL。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* insertionSortList(struct ListNode* head){
if(head == NULL && head->val == NULL)
{
return head;
}
struct ListNode* sorthead = head, *cur = head->next;
head->next = NULL;
while(cur)
{
struct ListNode* next = cur->next;
//是不是头结点
if(cur->val < sorthead->val)
{
cur->next = sorthead;
sorthead = cur;
}
else
{
struct ListNode* p = sorthead, *pre = NULL;
while(p)
{
if(cur->val < p->val)
{
pre->next = cur;
cur->next = p;
break;
}
pre = p;
p = p->next;
}
if(p == NULL)
{
pre->next = cur;
cur->next = NULL;
}
}
cur = next;
}
return sorthead;
}
几个指针存在的意义:
cur : 要依次往后遍历知道所有元素插入排序
next : 方便cur找到下一个位置
pre : 为了能插入(插入元素要找到前后位置)
p : 既为了能插入,也是判断结点有没有插入进去(循环结束p不为NULL就说明已经插入完,没有就说明要插入的元素是最大的)
3、删除排序链表中的重复元素Ⅰ
题目给定条件是已经排好序,那么就说明相同元素一定排在一起
那么就比较容易了,用两个指针
如果cur和next相同,让next往后跳,直到不相同,再让cur移动到next,重复上述操作
要注意的是后面全是一样也就是next直接走到空的情况,所以要判断next是否为空
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
struct ListNode* cur = head;
while(cur)
{
struct ListNode* next = cur->next;
while(next && cur->val == next->val)
{
next = next->next;
}
cur->next = next;
cur = next;
}
return head;
}
4、删除排序链表中的重复元素Ⅱ
这道题跟上一道的区别是这道题只要有相同的元素就把所有这个元素的结点都删除。
为了删除后链表跟前面的相连,所以这道题比上一道题多了个指针。
大致思想:
用cur和next俩指针迭代往后走,碰到相同的就让next往后走,再让cur走到next,删除掉前面相同的结点,再用prev链接
一些特殊情况
1️⃣有可能前面几个是相同的,此时prev是空,无法使用,头结点就要换位置
2️⃣所有元素都是相同的,所以就用next判断是否结束循环
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
if(head == NULL)
{
return NULL;
}
struct ListNode* cur = head, *next = cur->next, *prev = NULL;
while(next)
{
if(cur->val == next->val)
{
while(next && cur->val == next->val)
{
next = next->next;
}
//删除
while(cur != next)
{
struct ListNode* del = cur;
cur = cur->next;
free(del);
}
if(prev == NULL)
{
head = cur;
}
else
{
prev->next = cur;
}
if(next)
{
next = next->next;
}
}
else
{
//迭代
prev = cur;
cur = next;
next = next->next;
}
}
return head;
}