链表练习题2

目录

1.给定一个链表,判断链表中是否有环。

1.1为什么slow走一步,fast走两步,他们一定会在环里面相遇?会不会永远追不上?请证明

1.2 slow走一步,fast走3步?走4步?走n步行不行?请证明

1.3求环的入口点

2.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

*(再做)3.给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

4.对链表进行插入排序

 5.删除链表中的重复的结点,重复的结点不保留,返回链表头指针


1.给定一个链表,判断链表中是否有环。

 

 

快慢指针,即慢指针一次走一步,快指针一次走两步,两个指针从链表其实位置开始运行,如果链表 带环则一定会在环中相遇,否则快指针率先走到链表的末尾
bool hasCycle(struct ListNode *head) {
    struct ListNode*slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)
        {
            return true;
        }
    } 
    return false;
}

 面试的时候可能出现的扩展问题:

1.1为什么slow走一步,fast走两步,他们一定会在环里面相遇?会不会永远追不上?请证明

假设slow进环的时候,fast和slow的距离是N。追击的过程中,fast往前走两步,slow走一步,他们每走一次,他们之间的距离缩小1。二者之间的距离缩小到0的时候相遇

1.2 slow走一步,fast走3步?走4步?走n步行不行?请证明

fast走三步:假设slow进环的时候,fast和slow的距离是N。追击的过程中,fast往前走三步,slow走一步,他们每走一次,他们之间的距离缩小2。N为偶数时可以相遇,N为奇数时差距变为-1,即C-1(C是环的长度,如果C-1也恰好是奇数,那么就永远追不上) 

总结,如果slow进环时,slow和fast的差距N是奇数,且环的长度是偶数,那么他们两个在环里面一直打圈,追不上;(下图即为追不上的情况)

 

1.3求环的入口点

 假设起点到入口点的距离是L,假设相遇点到入口点的距离是X。假设环的大小是C

慢指针走的路程是:L+X(不可能为L+X+N*C,因为快慢指针的差距没有环大,slow进环以后在一圈之内fast就会追上slow,所以slow走的距离就是L+X)

快指针走的路程是:L+X+C

错误推论:

 2(L+X) = L+C  即 L+X = C  即L = C-X

如果L很长,环很小,那么slow指针进环时,假设快指针已经转了N圈了

2(L+X) = L + N*C + X

L = N*C - X

结论:一个指针从相遇点走,一个指针从起点走,他们会在入口相遇

2.给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 NULL

struct ListNode *detectCycle(struct ListNode *head) {
    struct ListNode*slow = head,*fast = head;
    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;
        if(slow == fast)
        {
            //相遇 
            struct ListNode*meet = fast;
            //通过推论证明,一个指针从head走
            //一个指针从meet走,他们会在入口点相遇
            while(meet != head)
            {
                meet = meet->next;
                head = head->next;
            }
            return meet;
        }
    }
    return NULL;
}

 思路二:假设slow和fast在环中某处相遇,把环从相遇点断开,转换成相交问题求交点

 

*(再做)3.给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的深度拷贝。

 

难点:拷贝结点的random不好处理,如果直接处理效率太低,太复杂

1. 拷贝以后,新链表需要确定每个random,需要去原有链表中去查找,是o(N),每个random都要处理就是o(N*N),效率就下来了

2.如果链表中存在结点的值相同,那么就更复杂了。列如有多个结点的值是7,那么原链表random指向7的结点,那么还要确定是第几个值为7的结点

1. 把拷贝的结点链接在原结点后面,建立链接关系

//1.拷贝结点挂在原结点的后面,建立对应关系
	struct Node* cur = head;
    while(cur)
    {
        struct Node*next = cur->next;
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));

        copy ->val = cur->val;
        cur->next = copy;
        copy->next = next;

        copy = next;
    }

 

 2.处理拷结点的random

 //2.处理copy结点的random
    cur = head;
    while(cur)
    {
        struct Node*copy = cur->next;
        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;//copy->random等于cur->random的拷贝结点
        }
        cur = copy->next;
    }

 3.把拷贝结点解下来,链接在一起

 //3.拷贝结点取下来链接到一起,恢复原链表
    cur = head;//每次cur从头开始走
    struct Node*copyHead,*copyTail;
    copyHead = copyTail = (struct Node*)malloc(sizeof(struct Node));//建立哨兵结点
    while(cur)
    {
        struct Node*copy = cur->next;
        struct Node*next = copy->next;

        //尾插
        copyTail->next = copy;
        copyTail = copyTail->next;

        cur->next = next;
        
        cur = next;
    }
    struct Node*guard = copyHead;
    copyHead = copyHead->next;
    free(guard);

    return head;

struct Node* copyRandomList(struct Node* head) {
    if(head == NULL)
       return NULL;
    //1.拷贝结点挂在原结点的后面,建立对应关系
	struct Node* cur = head;
    while(cur)
    {
        struct Node*next = cur->next;
        struct Node* copy = (struct Node*)malloc(sizeof(struct Node));

        copy ->val = cur->val;
        cur->next = copy;
        copy->next = next;

        copy = next;
    }
    //2.处理copy结点的random
    cur = head;
    while(cur)
    {
        struct Node*copy = cur->next;
        if(cur->random == NULL)
        {
            copy->random = NULL;
        }
        else
        {
            copy->random = cur->random->next;//copy->random等于cur->random的拷贝结点
        }
        cur = copy->next;
    }
    //3.拷贝结点取下来链接到一起,恢复原链表
    cur = head;//每次cur从头开始走
    struct Node* copyHead,*copyTail;
    copyHead = copyTail = (struct Node*)malloc(sizeof(struct Node));//建立哨兵结点
    while(cur)
    {
        struct Node*copy = cur->next;
        struct Node*next = copy->next;

        //尾插
        copyTail->next = copy;
        copyTail = copyTail->next;

        cur->next = next;
        
        cur = next;
    }
    struct Node*guard = copyHead;
    copyHead = copyHead->next;
    free(guard);

    return copyHead;
}

4.对链表进行插入排序

 

 

struct ListNode* insertionSortList(struct ListNode* head){
    if(head == NULL || head->next == NULL)
       return head;

    //1.初始条件
    struct ListNode* sortHead = head;
    struct ListNode*cur = head->next;
    sortHead->next  = NULL;

    while(cur)//2.终止条件
    {
        //3、迭代条件
        struct ListNode*next = cur->next;

        //将cur结点插入到前面的有序区间
        struct ListNode*p = NULL,*c = sortHead;
        while(c)
        {
             if(cur->val < c->val)
             {
                break;
             }
             else
             {
                p = c;
                c = c->next;
             }
        }
        if(p == NULL)
        {
            cur->next = c;
            sortHead = cur;
        }
        else
        {
            p->next = cur;
            cur->next = c;
        }
         cur = next;
    }
    return  sortHead;
}

 5.删除链表中的重复的结点,重复的结点不保留,返回链表头指针

 

 

 

 光有cur和next指针不行,还需添加一个prev前驱指针

 

 

 

 

 

 下面进行针对性分析:头尾都有连续数据相同的特殊场景

 

 

 

 此时prev是空指针,无法指向cur,应添加一个头指针,或者将头指针指向cur的位置(下面采取第二种方法)

 

class Solution {
public:
	/**
	 *
	 * @param head ListNode类
	 * @return ListNode类
	 */
	ListNode* deleteDuplication(ListNode* phead) {
		// write code here
		if (pHead == NULL || pHead->next == NULL)
			return pHead;
		//起始条件
		struct ListNode* prev = NULL, *cur = pHead, * next = pHead->next;
		while (next)
		{
			if (cur->val == next->val)
			{
				//删除重复
				while (next && cur->val == next->val)//next往后走,找到和cur不相等的位置
				{
					next = next->next;
				}
				//删掉cur到next之间的结点
				while (cur != next)
				{
					struct ListNode* del = cur;
					cur = cur->next;
					free(del);
				}
				if (prev == NULL)\
				{
					pHead = cur;
				}
				else
				{
					prev->next = cur;
				}

				if (next)
					next = next->next;
			}
			else
			{
				prev = cur;
				cur = next;
				next = next->next;
			}
		}
		return pHead;
	}
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值