【LeetCode】【C】- 16道强有力的算法题图解(1)

发布于:2022-12-30 ⋅ 阅读:(446) ⋅ 点赞:(0)

🐱作者:傻响
🐱专栏:《数据结构与算法》
🔥格言:你只管努力,剩下的交给时间!

                                                     

目录

🙉【LeetCode】[17.04]. 消失的数字

🙊第一种解题方法:《排序》

🙊第一种解题方法:《相减法》

🙊第三种解题方法:《异或法》

🙉【LeetCode】[17.04]. 消失的数字

🙉【LeetCode】[27]. 移除元素

🙉【LeetCode】[26].删除有序数组中的重复项

🙉【LeetCode】[88]. 合并两个有序数组

🙉【LeetCode】[203].移除链表元素

🙊第一种解题方式:《双指针》

🙊第二种解题方式:《将不是val的值尾插到新链表》

🙊第三种解题方式:《带哨兵头》

🙉【LeetCode】[21].合并两个有序链表

🙉【LeetCode】[206].反转链表

🙊第一种解题思路:《取节点到新的链表》

🙊第二种解题思路:《反转链表指针方向》

🙉【LeetCode】[876].链表的中间节点

🙉【LeetCode】[22].链表中倒数第k个节点

🙉【LeetCode】[02.04].[分割链表]

🙉【LeetCode】[234].回文链表

🙉【LeetCode】[160].相交链表

🙉【LeetCode】[141].环形链表

🙉【LeetCode】[142].环形链表 II

🙊第一种解题思路:《公式证明》

🙊第二种解题思路:《转换成相交问题》

🙉【LeetCode】[138].复制带随机指针的链表

🙈第一步代码的实现

🙈第二步代码的实现

🙈第三步代码的实现

🙈完整代码


🙉【LeetCode】[17.04]. 消失的数字

题目链接:面试题 17.04. 消失的数字

数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

注意:本题相对书上原题稍作改动

示例 1:

输入:[3,0,1]
输出:2

示例 2:

输入:[9,6,4,2,3,5,7,0,1]
输出:8

🙊第一种解题方法:《排序》

// 第一种方式:排序的方式(时间都原因无法通过)
int missingNumber(int* nums, int numsSize)
{
    // 排序
    for (int i = 0; i < numsSize - 1; i++)
    {
        for (int j = 0; j < numsSize - 1 - i; j++)
        {
            if (nums[j] > nums[j + 1])
            {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
    }
​
    // 寻找数组中数字加1的对数
    for (int i = 0; i < numsSize; i++)
    {
        if (nums[i] + 1 != nums[i + 1])
            return nums[i]+1;
    }
    return -1;
}

🙊第一种解题方法:《相减法》

        将0~N个数组加到一起结果是:result1。 ​ 在把数组中数的数加到一起结果是:result2。 ​ result1-result2 就是我们要找到的数。

// 第二种方式:相减法(时间都原因无法通过)
int missingNumber2(int* nums, int numsSize)
{
    int result1 = 0, result2 = 0;
    // 将0~N个数组加到一起结果是
    for (int i = 0; i < numsSize + 1; i++)
    {
        result1 += i;
    }
    // 在把数组中数的数加到一起结果是
    for (int i = 0; i < numsSize; i++)
    {
        result2 += nums[i];
    }
    // 属于的值就是缺少的那个数值。
    return result1 - result2;
​
}

🙊第三种解题方法:《异或法》

异或的方式。

先与数组中的数值进行异或。

在与数组中0~N进行异或。

int missingNumber3(int* nums, int numsSize)
{
    int x = 0;
    // 在本数组中进行异或。
    for (int i = 0; i < numsSize; i++)
    {
        x ^= nums[i];
    }
​
    // 在和0~N所有的数进行异或
    for (int i = 0; i < numsSize + 1; i++)
    {
        x ^= i;
    }
    return x;
}

🙉【LeetCode】[17.04]. 消失的数字

题目链接:189. 轮转数组

给你一个数组,将数组中的元素向右轮转 k 个位置,其中 k 是非负数。

示例 1:

输入: nums = [1,2,3,4,5,6,7], k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右轮转 1 步: [7,1,2,3,4,5,6]
向右轮转 2 步: [6,7,1,2,3,4,5]
向右轮转 3 步: [5,6,7,1,2,3,4]

示例 2:

输入:nums = [-1,-100,3,99], k = 2
输出:[3,99,-1,-100]
解释: 
向右轮转 1 步: [99,-1,-100,3]
向右轮转 2 步: [3,99,-1,-100]

提示:

  • 1 <= nums.length <= 105

  • -231 <= nums[i] <= 231 - 1

  • 0 <= k <= 105

 

 

void Reverse(int* nums, int begin, int end)
{
    while (begin < end)
    {
        int temp = *(nums + begin);
        *(nums + begin) = *(nums + end);
        *(nums + end) = temp;
        begin++;
        end--;
    }
}
void Rotate(int* nums, int numsSize, int k)
{
    if (k >= numsSize)
    {
        k %= numsSize;
    }
    // 左旋转
    ///Reverse(nums, 0, numsSize - 1 - k);
    //Reverse(nums, numsSize - k, numsSize - 1);
    //Reverse(nums, 0, numsSize - 1);
    // 右旋转
    Reverse(nums, 0, k - 1);
    Reverse(nums, k, numsSize - 1);
    Reverse(nums, 0, numsSize - 1);
 
}

🙉【LeetCode】[27]. 移除元素

题目链接:27. 移除元素 - 力扣(LeetCode)

描述:

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参作任何拷贝
int len = removeElement(nums, val);
​
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。
for (int i = 0; i < len; i++) {
    print(nums[i]);
}

实例1:

输入:nums = [3,2,2,3], val = 3
输出:2, nums = [2,2]
解释:函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。你不需要考虑数组中超出新长度后面的元素。例如,函数返回的新长度为 2 ,而 nums = [2,2,3,3] 或 nums = [2,2,0,0],也会被视作正确答案。

实例2:

输入:nums = [0,1,2,2,3,0,4,2], val = 2
输出:5, nums = [0,1,4,0,3]
解释:函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4。注意这五个元素可为任意顺序。你不需要考虑数组中超出新长度后面的元素。

提示:

  • 0 <= nums.length <= 100

  • 0 <= nums[i] <= 50

  • 0 <= val <= 100

题目解析:

标准代码:

int removeElement(int* nums, int numsSize, int val)
{
    // 定义一个来源和目标。
    int source = 0, destination = 0;
​
    // 遍历整个数组,长度小于numsSize
    while(source < numsSize)
    {
        // 如果元素的值和val不相等(那就不是要删除的值),那就拿这个值拿到destination指向的数组位置上。
        if(nums[source] != val)
        {
            *(nums + destination) = *(nums + source);
            ++source;
            ++destination;
        }
        else    // 如果是的话就不让destination指向的数组位置上放,source继续向前走。
        {
            ++source;
        }
    }
​
    return destination;
}

🙉【LeetCode】[26].删除有序数组中的重复项

题目链接:26. 删除有序数组中的重复项 - 力扣(LeetCode)

描述:

给你一个 升序排列 的数组 nums ,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。

由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k 个元素,那么 nums 的前 k 个元素应该保存最终结果。

将最终结果插入 nums 的前 k 个位置后返回 k 。

不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

判题标准:

系统会用下面的代码来测试你的题解:

int[] nums = [...]; // 输入数组
int[] expectedNums = [...]; // 长度正确的期望答案
​
int k = removeDuplicates(nums); // 调用
​
assert k == expectedNums.length;
for (int i = 0; i < k; i++) {
    assert nums[i] == expectedNums[i];
}

如果所有断言都通过,那么您的题解将被 通过

实例1:

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。

实例2:

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

提示:

  • 1 <= nums.length <= 3 * 104

  • -104 <= nums[i] <= 104

  • nums 已按 升序 排列

题目解析:

标准代码:

int removeDuplicates(int* nums, int numsSize)
{
    // 定义一个来源和目标。
    int source = 0, destination = 0;
​
     // 遍历整个数组,长度小于numsSize
    while(source < numsSize)
    {
        // 如果source指向数组的位置和destination指向数组的位置值是一样的,说明是重复的source前进。
        if(nums[source] == nums[destination])
        {
            ++source;
        }
        else// 如果source指向数组的位置和destination指向数组的位置值不是一样的,
        {   // 让destination先++ 这样destination就移动了下一个位置,让source复制。
            nums[++destination] = nums[source++];
        }
    }
    // destination永远少移动一下,所以返回的是后加1.
    return destination + 1;
}

🙉【LeetCode】[88]. 合并两个有序数组

题目链接:88. 合并两个有序数组 - 力扣(LeetCode)

给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2,另有两个整数 m 和 n ,分别表示 nums1 和 nums2 中的元素数目。

请你 合并 nums2 到 nums1 中,使合并后的数组同样按 非递减顺序 排列。

注意:最终,合并后数组不应由函数返回,而是存储在数组 nums1 中。为了应对这种情况,nums1 的初始长度为 m + n,其中前 m 个元素表示应合并的元素,后 n 个元素为 0 ,应忽略。nums2 的长度为 n 。

实例1:

输入:nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
输出:[1,2,2,3,5,6]
解释:需要合并 [1,2,3] 和 [2,5,6] 。
合并结果是 [1,2,2,3,5,6] ,其中斜体加粗标注的为 nums1 中的元素。

实例2:

输入:nums1 = [1], m = 1, nums2 = [], n = 0
输出:[1]
解释:需要合并 [1] 和 [] 。
合并结果是 [1] 。

实例3:

输入:nums1 = [0], m = 0, nums2 = [1], n = 1
输出:[1]
解释:需要合并的数组是 [] 和 [1] 。
合并结果是 [1] 。
注意,因为 m = 0 ,所以 nums1 中没有元素。nums1 中仅存的 0 仅仅是为了确保合并结果可以顺利存放到 nums1 中。

提示:

  • nums1.length == m + n

  • nums2.length == n

  • 0 <= m, n <= 200

  • 1 <= m + n <= 200

  • -109 <= nums1[i], nums2[j] <= 109

题目解析:

标准代码:

void merge(int* nums1, int nums1Size, int m, int* nums2, int nums2Size, int n)
{
    // 定义指针的位置。
    int end = m + n - 1;
    int end1 = m - 1, end2 = n - 1;
​
    // 遍历整个数组,两个指针都不小于0
    while(end1 >= 0 && end2 >= 0)
    {
        // 数组nums1的end1指向的元素大于nems2的end2指向的元素,将nums2的值传递给nums1的end指向的元素。
        if(nums1[end1] > nums2[end2])
        {
            nums1[end--] = nums1[end1--];
        }        
        else    // 相反
        {
             nums1[end--] = nums2[end2--];
        }
    }
​
​
    // 程序走到这里总有一组数据传递完成。
    // 如果end2对应的nums2数组传递完成了,那么就不需要进行处理了。
​
    // 如果end1对应的num1数组传递完成了,那么这时候需要进行处理把nums2剩余的值继续传递完成。
    while(end2 >= 0)
    {
        nums1[end--] = nums2[end2--];
    }
}

🙉【LeetCode】[203].移除链表元素

题目链接:203. 移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点

实例1:

输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]

示例 2:

输入:head = [], val = 1
输出:[]

示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]

🙊第一种解题方式:《双指针》

 

// 第一种方式
struct ListNode* removeElements(struct ListNode* head, int val)
{
   struct ListNode *pCurrent = head, *pPrev = NULL;
​
    // pCurrent == NULL 代表链表遍历完了。
    while(pCurrent != NULL)
    {
        // 1、节点值和val值不相等。
        // 2、节点值与val值相等。
        if(pCurrent->val != val)
        {
            pPrev = pCurrent;
            pCurrent = pCurrent->next;
        }
        else
        {
            // 1、非头删除
            // 2、头删除
            if(pCurrent != head)
            {
                pPrev->next = pCurrent->next;
                free(pCurrent);
                pCurrent = pPrev->next;
            }
            else
            {
                head = head->next;
                free(pCurrent);
                pCurrent = head;
            }
        }
    }
    // 返回新的头节点
    return head; 
}

🙊第二种解题方式:《将不是val的值尾插到新链表》

 

 

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* pCurrent = head;
    struct ListNode* newNode = NULL;
    struct ListNode* pTail = NULL;
​
    while (pCurrent != NULL)
    {
        // pCurrent-val = val / pCurrent-val != val 两种情况。
        if (pCurrent->val != val)
        {
            // 等于空的话创建新的节点。
            if (pTail == NULL)
            {
                newNode = pTail = pCurrent;
            }
            else
            {
                pTail->next = pCurrent;
                pTail = pTail->next;
            }
            pCurrent = pCurrent->next;
        }
        else
        {
            struct ListNode* del = pCurrent;
            pCurrent = pCurrent->next;
            free(del);
            del = NULL;
        }
    }
​
    if (pTail != NULL)
        pTail->next = NULL;
​
    // 返回新的头节点。
    return newNode;
}

🙊第三种解题方式:《带哨兵头》

struct ListNode* removeElements(struct ListNode* head, int val)
{
    struct ListNode* pCurrent  = head;
    // 定义哨兵头
    struct ListNode* pGuardHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* pTail = pGuardHead;
​
    while(pCurrent != NULL)
    {
        //1、pCurrent->val == val 
        //2、pCurrent->val != val
        if(pCurrent->val != val)
        {
            // 哨兵下一个节点指向pCurrent
            pTail->next = pCurrent;
​
            // 两个节点都进行下移动
            pTail = pTail->next;
            pCurrent = pCurrent->next;
        }
        else
        {
            struct ListNode* del = pCurrent;
            pCurrent = pCurrent->next;
            free(del);
            del = NULL;
        }
​
    }
​
    if(pTail != NULL)
        pTail->next = NULL;
    
    // 返还头节点,将哨兵位释放掉。
    head = pGuardHead->next;
    free(pGuardHead);
    pGuardHead = NULL;
    return head;
}

🙉【LeetCode】[21].合并两个有序链表

题目链接:21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例 2:

输入:l1 = [], l2 = []
输出:[]

示例 3:

输入:l1 = [], l2 = [0]
输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]

  • -100 <= Node.val <= 100

  • l1 和 l2 均按 非递减顺序 排列

 

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2)
{
    // 创建哨兵位节点。
    struct ListNode* pGuardHead = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(pGuardHead == NULL)
    {
        perror("mergeTwoLists malloc fail!\n");
        exit(-1);
    }
    else
    {
        pGuardHead->next = NULL;
    }
    struct ListNode* pTail = pGuardHead;
​
    // 两个链表的当前指针。
    struct ListNode* pCurrent1 = list1;
    struct ListNode* pCurrent2 = list2;
    
    while(pCurrent1 != NULL && pCurrent2 != NULL)
    {
        // 比较两个节点那个节点内容小,小的放入新的节点上
        if(pCurrent1->val < pCurrent2->val)
        {
            pTail->next = pCurrent1;
            pCurrent1 = pCurrent1->next;
        }
        else
        {
             pTail->next = pCurrent2;
            pCurrent2 = pCurrent2->next;
        }
        pTail = pTail->next;
    }
​
    // 判断谁没有走完
    if(pCurrent1 != NULL)
        pTail->next =pCurrent1;
    else if (pCurrent2 != NULL)
        pTail->next =pCurrent2;
​
    // 返回初始节点,要释放桥接的哨兵头
    struct ListNode* head = pGuardHead->next;
    free(pGuardHead);
    pGuardHead = NULL;
​
    return head;
}

🙉【LeetCode】[206].反转链表

题目链接:206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

 

示例 3:

输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]

  • -5000 <= Node.val <= 5000

🙊第一种解题思路:《取节点到新的链表》

// 第一种解题思路
struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode* pCurrent = head;
    struct ListNode* newNode = NULL;
​
    while(pCurrent != NULL)
    {   
        // 保存pCurrent->指向的下一个位置。
        struct ListNode* next = pCurrent->next;
​
        // pCurrent->next 让他指向NULL,这是newNode还是空。
        pCurrent->next = newNode;
        // newNode指向pCurrent的位置。
        newNode = pCurrent;
        // pCurrent在继续往下移动
        pCurrent = next;
    } 
    // 程序走到这里就反转完成啦。
    return newNode;
}

🙊第二种解题思路:《反转链表指针方向》

struct ListNode* reverseList(struct ListNode* head)
{
    struct ListNode *n1,*n2,*n3;
    n1 = NULL;
    n3 = NULL;
    n2 = head;
​
    while(n2 != NULL)
    {
        // n3记录下一个节点的位置。
        n3 = n2->next;
        // n2指向前面的节点。
        n2->next = n1;
​
        // 值传完,开始迭代。
        n1 = n2;
        n2 = n3;
​
​
    }
​
    return n1;
}

🙉【LeetCode】[876].链表的中间节点

题目链接:876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:

给定链表的结点数介于 1 和 100 之间。

struct ListNode* middleNode(struct ListNode* head)
{
    struct ListNode* slow = head;
    struct ListNode* fast = head;
​
    // 慢指针一次走两步,快指针一次走一步
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
    }
    // 走完满指针就是中间值。
    return slow;
}

🙉【LeetCode】[22].链表中倒数第k个节点

题目链接:剑指 Offer 22. 链表中倒数第k个节点

题目链接:链表中倒数第k个结点牛客题霸牛客网 (nowcoder.com)

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.
​
返回链表 4->5.

 

struct ListNode* FindKthToTail(struct ListNode* pListHead, int k ) 
{
    struct ListNode* slow = pListHead;
    struct ListNode* fast = pListHead;
​
    // fast 先走k步
    while(k--)
    {
        // 如果说在fast先走k步的时候,出现空了,那么就会有问题了。
        if(fast == NULL)
            return NULL;
        
        fast = fast->next;
    }
​
    // slow 和 fast一起走
    while(fast != NULL)
    {
        fast = fast->next;
        slow = slow->next;
    }
    // 返回慢走的指针
    return slow;
}

🙉【LeetCode】[02.04].[分割链表]

题目链接:面试题 02.04. 分割链表

题目链接:链表分割牛客题霸牛客网 (nowcoder.com)

给你一个链表的头节点 head 和一个特定值 x ,请你对链表进行分隔,使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。

你不需要 保留 每个分区中各节点的初始相对位置。

示例 2:

输入:head = [2,1], x = 2
输出:[1,2]

提示:

  • 链表中节点的数目在范围 [0, 200] 内

  • -100 <= Node.val <= 100

  • -200 <= x <= 200


 
struct ListNode* partition(struct ListNode* head, int x)
{
    struct ListNode* pSmallerGruad, *pSmallerTail;      // 定较小的哨兵头,和尾指针
    struct ListNode* pGreaterGruad, *pGreaterTail;      // 定较大的哨兵头,和尾指针
    // 判断是否开辟成功!
    pSmallerGruad = pSmallerTail = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(pSmallerGruad == NULL)
    {
        perror("partition pSmallerGruad malloc fail!");
        exit(-1);
    }
    else
    {
        pSmallerGruad->next = NULL;
    }
​
​
    pGreaterGruad = pGreaterTail = (struct ListNode*)malloc(sizeof(struct ListNode));
    if(pGreaterGruad == NULL)
    {
        perror("partition pGreaterGruad malloc fail!");
        exit(-1);
    }
    else
    {
        pGreaterGruad->next = NULL;
    }
​
    
    // 比哪里链表,小于x的和pSmallerGruad链接,大于x的和pGreaterGruad链接。
    struct ListNode* pCurrent = head;
    while(pCurrent != NULL)
    {
        if(pCurrent->val < x)
        {
            pSmallerTail->next = pCurrent;
            pSmallerTail = pSmallerTail->next;
        }
        else
        {
            pGreaterTail->next = pCurrent;
            pGreaterTail = pGreaterTail->next;
        }
        // 遍历节点继续前进。
        pCurrent = pCurrent->next;
    }
​
    // 将较小的最后一个和较大的第一个进行链接,并且将较大的最后一个设为空。
    pSmallerTail->next = pGreaterGruad->next;
    pGreaterTail->next =NULL;
​
    // 返还节点
    head = pSmallerGruad->next;
    // 释放空间。
    free(pSmallerGruad);
    pSmallerGruad = NULL;
    free(pGreaterGruad);
    pGreaterGruad = NULL;
​
    return head;
}

🙉【LeetCode】[234].回文链表

题目链接:234. 回文链表

题目链接:链表的回文结构牛客题霸牛客网 (nowcoder.com)

这道题我们可以采用之前写的函数。

给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false

 

 

// 查找中间节点。
struct ListNode* middleNode(struct ListNode* pHead)
{
    struct ListNode* slow, * fast;
    slow = fast = pHead;
​
    while (fast != NULL && fast->next != NULL)
    {
        fast = fast->next -> next;
        slow = slow->next;
    }
​
    return slow;
}
​
// 反转节点。
struct ListNode* ReverseList(struct ListNode* pHead)
{
    struct ListNode* newNode = NULL;
    struct ListNode* pCurrent = pHead;
​
    while (pCurrent != NULL)
    {
        struct ListNode* next = pCurrent->next;
​
        pCurrent->next = newNode;
        newNode = pCurrent;
​
        pCurrent = next;
    }
​
    return newNode;
}
​
// 题目函数
bool isPalindrome(struct ListNode* head)
{
    struct ListNode* pCurrent = head;
​
    // 找节点的中间节点
    struct ListNode* middle = middleNode(pCurrent);
    /// 中间节点向后的节点逆置
    struct ListNode* rMiddle = ReverseList(middle);
​
    // 遍历整个链表,两两对比,只要一对不相等就不是回文链表。
    while(pCurrent != NULL && rMiddle != NULL)
    {
        if(pCurrent->val != rMiddle->val)
            return false;
        pCurrent = pCurrent->next;
        rMiddle = rMiddle->next;
    }
​
    return true;
}

🙉【LeetCode】[160].相交链表

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构 。

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0

  • listA - 第一个链表

  • listB - 第二个链表

  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数

  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

 

 

提示:

  • listA 中节点数目为 m

  • listB 中节点数目为 n

  • 1 <= m, n <= 3 * 104

  • 1 <= Node.val <= 105

  • 0 <= skipA <= m

  • 0 <= skipB <= n

  • 如果 listA 和 listB 没有交点,intersectVal 为 0

  • 如果 listA 和 listB 有交点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

 

 

struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    // 检测链表不是NULL链表。
    if(headA == NULL || headB == NULL)
        return NULL;
    
    
    // 定义遍历节点变量。
    struct ListNode* pCurrentA = headA;
    struct ListNode* pCurrentB = headB;
​
    // 求A组的长度
    int lengthA = 0 ;
    while(pCurrentA != NULL)
    {
        pCurrentA = pCurrentA->next;
        lengthA++;
    }
​
    // 求B组的长度
    int lengthB = 0;
    while(pCurrentB != NULL)
    {
        pCurrentB = pCurrentB->next;
        lengthB++;
    }
​
    // 判断是否相等
    if(pCurrentA != pCurrentB)
    {
        return NULL;
    }
​
​
    // 假设A组的长,B组的短
    struct ListNode* longList = headA;
    struct ListNode* shortList = headB;
    if(lengthA < lengthB) // 这里进行确认
    {
        longList = headB;
        shortList = headA;
    }
​
    // 长的先走 差步
    int excursion = abs(lengthA - lengthB);
    while(excursion--)
    {
        longList = longList->next;
    }
    // 找第一个相同的节点,那个就是相交点。
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
​
    // 随便返回节点就行。
    return longList;
}

🙉【LeetCode】[141].环形链表

题目链接:141. 环形链表

给你一个链表的头节点 head ,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。

如果链表中存在环 ,则返回 true 。 否则,返回 false 。

提示:

  • 链表中节点的数目范围是 [0, 104]

  • -105 <= Node.val <= 105

  • pos-1 或者链表中的一个 有效索引

bool hasCycle(struct ListNode *head)
{
    struct ListNode *fast, *slow;
    fast = slow = head;
    // fast走两步,slow走一步。
    while(fast != NULL && fast->next != NULL)
    {
        fast = fast->next->next;
        slow = slow->next;
        if(fast == slow)
        {
            return true;
        }
    }
    // 如果fast走到空了,那就说明不是环形了。
    return false;
}
​
// 延申:
// 请证明一下,slow一次走一步,fast一次走两步?
// 请证明一下,slow一次走一步,fast一次走三步?
// 请证明一下,slow一次走一步,fast一次走N步?
// 请证明一下,slow一次走M步,fast一次走N步?(M<N)

🙉【LeetCode】[142].环形链表 II

题目链接:142. 环形链表 II

给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

不允许修改 链表。

提示:

  • 链表中节点的数目范围在范围 [0, 104]

  • -105 <= Node.val <= 105

  • pos 的值为 -1 或者链表中的一个有效索引

🙊第一种解题思路:《公式证明》

struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode *slow, *fast;
    slow = fast = head;
    
    // 先找到slow和fast的相交点。
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        
        // 找到相交点 
        if(fast == slow)
        {
            struct ListNode* pCurrent = head;
            struct ListNode* meet = slow;
            // pCurrent 和 meet 相等就是入口点。
            while(meet != pCurrent)
            {
                meet = meet->next;
                pCurrent = pCurrent->next;
            }
            return meet;
        }
    }
    return NULL;
}

🙊第二种解题思路:《转换成相交问题》

利用相交的函数。

// 利用链表相交的函数。
struct ListNode *getIntersectionNode(struct ListNode *headA, struct ListNode *headB) 
{
    // 检测链表不是NULL链表。
    if(headA == NULL || headB == NULL)
        return NULL;
    
    
    // 定义遍历节点变量。
    struct ListNode* pCurrentA = headA;
    struct ListNode* pCurrentB = headB;
​
    // 求A组的长度
    int lengthA = 0 ;
    while(pCurrentA != NULL)
    {
        pCurrentA = pCurrentA->next;
        lengthA++;
    }
​
    // 求B组的长度
    int lengthB = 0;
    while(pCurrentB != NULL)
    {
        pCurrentB = pCurrentB->next;
        lengthB++;
    }
​
    // 判断是否相等
    if(pCurrentA != pCurrentB)
    {
        return NULL;
    }
​
​
    // 假设A组的长,B组的短
    struct ListNode* longList = headA;
    struct ListNode* shortList = headB;
    if(lengthA < lengthB) // 这里进行确认
    {
        longList = headB;
        shortList = headA;
    }
​
    // 长的先走 差步
    int excursion = abs(lengthA - lengthB);
    while(excursion--)
    {
        longList = longList->next;
    }
    // 找第一个相同的节点,那个就是相交点。
    while(longList != shortList)
    {
        longList = longList->next;
        shortList = shortList->next;
    }
​
    // 随便返回节点就行。
    return longList;
}
​
​
​
struct ListNode *detectCycle(struct ListNode *head)
{
    struct ListNode *slow, *fast;
    slow = fast = head;
    
    // 先找到slow和fast的相交点。
    while(fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        
        // 找到第一个交点 
        if(fast == slow)
        {
            struct ListNode* meet = slow;
            struct ListNode* next = meet->next; // 记录要断开的节点
            meet->next = NULL; // 使节点断开
​
            // 求入口的相交点。
            struct ListNode* entrance = getIntersectionNode(head,next);
            meet->next = next; // 恢复节点
            return entrance;
        }
    }
    return NULL;
}

🙉【LeetCode】[138].复制带随机指针的链表

题目链接:138. 复制带随机指针的链表

给你一个长度为 n 的链表,每个节点包含一个额外增加的随机指针 random ,该指针可以指向链表中的任何节点或空节点。

构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的 next 指针和 random 指针也都应指向复制链表中的新节点,并使原链表和复制链表中的这些指针能够表示相同的链表状态。复制链表中的指针都不应指向原链表中的节点 。

例如,如果原链表中有 X 和 Y 两个节点,其中 X.random --> Y 。那么在复制链表中对应的两个节点 x 和 y ,同样有 x.random --> y 。

返回复制链表的头节点。

用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。 random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。 你的代码 只 接受原链表的头节点 head 作为传入参数。

提示:

  • 0 <= n <= 1000

  • -104 <= Node.val <= 104

  • Node.randomnull 或指向链表中的节点。

先看一下解题思路:

🙈第一步代码的实现

🙈第二步代码的实现

🙈第三步代码的实现

🙈完整代码

​
struct Node* copyRandomList(struct Node* head)
{
    // 1、插入copy的节点
    struct Node* pCurrent = head;
    struct Node* copy = NULL;
    struct Node* next = NULL;
​
    while(pCurrent != NULL)
    {
        next = pCurrent->next;   // 记录pCurrent的下一个节点。
        copy = (struct Node*)malloc(sizeof(struct Node)); // 创建新的节点。
        copy->val = pCurrent->val; // 值拷贝
​
        // 插入pCurrent 和 pCurrent->next中间
        pCurrent->next = copy;
        copy->next = next;
​
        // 迭代;
        pCurrent = next;      
    }
​
​
    // 2、更新copy的random
    pCurrent = head;
    while(pCurrent != NULL)
    {
        copy = pCurrent->next;
        
        // 更新random有两种情况:
        // 1、如果原节点random是NULL 那么拷贝节点的random也是NULL
        // 2、如果源节点random不是NULL 那么拷贝节点的random就是源节点的下一个节点。
        if(pCurrent->random == NULL)
            copy->random = NULL;
        else
            copy->random = pCurrent->random->next;
​
        // 迭代
        pCurrent = copy->next;
    }
​
​
    // 3、copy节点要摘下来链接在一起,恢复原节点。
    pCurrent = head;
    struct Node* copyHead = NULL;
    struct Node* copyTail = NULL;
    while(pCurrent)
    {
        // 定义位置。
        copy = pCurrent->next;
        next = copy->next;
        // 取节点尾插
        // 1、还没有任何节点
        // 2、已经右节点了
        if(copyTail == NULL)
            copyHead = copyTail = copy;
        else
        {
            copyTail->next = copy;
            copyTail = copyTail->next;
        }
​
        // 恢复原链表
        pCurrent->next = next;
​
        // 迭代
        pCurrent = pCurrent->next;
    }
​
    return copyHead;
}

网站公告

今日签到

点亮在社区的每一天
去签到