单链表
练习:移除链表元素
给你一个链表的头节点 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段链表的尾节点