数据结构—链表练习题

单链表

练习:移除链表元素

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

示例1:

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

struct ListNode* removeElements(struct ListNode* head, int val) {
    struct ListNode* prev = NULL, * cur = head;
    while (cur)//cur从头开始往后找要删除的节点
    {
        if (cur->val == val)//如果找到了要删除的节点
        {
            if (cur == head)//如果找到的要删除的节点刚好就是头,就要进行头删
            {
                head = cur->next;//头删就得换头
                free(cur);//换好头后,把原来的头给释放
                cur = head;//cur又得从新的头开始往后找还有没有需要删除的节点
            }
            else//如果找到的要删除的节点不是头,就进行常规的中间删除或尾删
            {//无需换头
                prev->next = cur->next;
                free(cur);
                cur = prev->next;
            }
        }
        else//否则没找到要删除的节点,cur往后走去找需要删除的节点
        {
            prev = cur;//prev永远跟在cur的前面,也就是cur往后走之前赋值给prev
            cur = cur->next;//cur再往后走
        }
    }
    return head;//最后如果的进行过头删,返回的是换头后的新,如果未进行过头删则返回的是旧头
}

为了能在VS中调试线上OJ题

针对OJ的报错

手动快速创建一个单链表,复制OJ代码到vs

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>

// Definition for singly-linked list.
 struct ListNode {
     int val;
     struct ListNode *next;
 };

int main()//手动创建一个单链表进行测试,调试即可测试removeElements函数
{//创建4个节点
    struct ListNode* node1 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* node2 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* node3 = (struct ListNode*)malloc(sizeof(struct ListNode));
    struct ListNode* node4 = (struct ListNode*)malloc(sizeof(struct ListNode));
    node1->val = 7;
    node2->val = 7;
    node3->val = 7;
    node4->val = 7;
    node1->next = node2;
    node2->next = node3;
    node3->next = node4;
    node4->next = NULL;

    struct ListNode* head = removeElements(node1, 7);
    return 0;
}

练习:反转链表

示例:

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

方法1:节点间指针的反转

struct ListNode* reverseList(struct ListNode* head) {
    if(head == NULL)
        return NULL;//空链表不需要反转
    struct ListNode* prev, *cur, *next;//创建3个指针
    prev = NULL;
    cur = head;
    next = head->next;//3个指针的起始地址(起始条件)
    while(cur)
    {
        cur->next = prev;//反转
        //迭代
        prev = cur;
        cur = next;
        if(next)//防止最后next越界
        {
            next = next->next;
        }
    }
    return prev;//最好画图理解
}

方法2:头插、换头

思路:取原链表中各节点依次头插到newhead新链表中

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* reverseList(struct ListNode* head) {
    //方式2:头插
    struct ListNode* newhead, *cur, *next;
    newhead = NULL;
    cur = head;
   // next = cur->next;
    while(cur)
    {
        next = cur->next;

        cur->next = newhead;//头插
        newhead = cur;//换头
        //迭代
        cur = next;
    }
    return newhead;//返回新头,最好画图理解
}

练习:寻找链表的中间节点

给你单链表的头结点 head ,请你找出并返回链表的中间结点。

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

画图分析:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode* middleNode(struct ListNode* head) {
    struct ListNode* slow = head;//快慢双指针
    struct ListNode* fast = head;//起始位置
    while(fast && fast->next)//fast不为NULL是有偶数个节点的情况,fast->next是奇数个的情况
    {
        slow = slow->next;//slow一次往后走一步
        fast = fast->next->next;//fast一次往后走两步
    }
    return slow;//最终slow指向的节点就是中间节点
}

练习:返回倒数第k个节点的值

思路:双指针,想方法让slow是相对于fast的倒数第k个节点,当fast是最后一个节点时,slow就是相对于最后一个节点是倒数第k个节点

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
int kthToLast(struct ListNode* head, int k){
    struct ListNode* fast, *slow;
    fast = head;
    slow = head;
    int i = 0;
    for(i = 0; i < k-1; i++)//让fast先走k-1步
    {
        fast = fast->next;
    }//此时slow就是相对于fast的倒数第k个节点
    //再让slow和fast同时往后走
    while(fast->next)//当fast走到最后一个节点
    {
        slow=slow->next;
        fast=fast->next;
    }
    return slow->val;//此时的slow就是相对于最后1个节点的倒数第k个节点
}

练习:求带环链表的入环节点

方法1:数学证明

思路先判断是否带环,若不带环就返回NULL,若带环再取求入环点

快慢指针从头开始走,慢指针每次走1步,快指针每走2步,若链表带环,他们一定会相遇,记录相遇点,一个指针从头开始走,另一个指针从快慢指针相遇点开始走,这俩指针每次都只走1步,他俩的相遇点就是入环点

以下证明过程来自比特杭哥,推荐大家购买比特就业课深入学习

 

 

 

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    //首先判断是否有环
    struct ListNode *fast = head, *slow = head;
    while(fast && fast->next)//若不带环,快指针会走到尾节点,若带环则根本没有尾
    {
        fast = fast->next->next;//快指针每次走2步
        slow = slow->next;//慢指针每次走1步
        if(fast == slow)//链表带环,快慢指针一定会相遇
        {
            struct ListNode *meet = fast;//记录相遇节点
            //一个指针从相遇点开始走,每次走1步,另一个指针从头开始走,每次走1步,他们的相遇点就是入环点
            while(meet != head)//没有相遇,就继续走
            {
                meet = meet->next;
                head = head->next;
            }
            return head;//相遇就记录相遇点,此相遇点就是入环点
        }
    }
    return NULL;//如果不带环就返回NULL
}

方法2:转换成求2段链表的交点

快慢指针从头开始走,慢指针每次走1步,快指针每走2步,若链表带环,他们一定会相遇,记录相遇点,把相遇点当成是2段链表的尾节点

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值