代码随想录

移除元素。不设置虚拟头节点,分类讨论。

struct ListNode* removeElements(struct ListNode* head, int val) {
struct ListNode* temp;
// 当头结点存在并且头结点的值等于val时
while (head && head->val == val) {
temp = head;
// 将新的头结点设置为head->next并删除原来的头结点
head = head->next;
free(temp);
}

struct ListNode* cur = head;
// 当cur存在并且cur->next存在时
// 此解法需要判断cur存在因为cur指向head。若head本身为NULL或者原链表中元素都为val的话,cur也会为NULL
while (cur && (temp = cur->next)) {//temp这个临时指针用于遍历链表。
//temp这个赋值表达式的返回值是 temp,在条件语句中用于判断是否继续循环。即当 cur 不为空且 cur->next 不为空时,条件为真,继续执行循环。
// 若cur->next的值等于val
if (temp->val == val) {
// 将cur->next设置为cur->next->next并删除cur->next
cur->next = temp->next;
free(temp);
}
// 若cur->next不等于val,则将cur后移一位
else
cur = cur->next;
}

// 返回头结点
return head;
}

设置虚拟头节点

   /**
 * 定义单链表的结构体
 * struct ListNode {
 *   int val;
 *   struct ListNode *next;
 * };
 */

 // 从链表中删除所有值为 'val' 的节点的函数
struct ListNode* removeElements(struct ListNode* head, int val) {
// 定义方便起见的 typedef
typedef struct ListNode ListNode;

// 创建一个新的节点作为伪头节点
ListNode* shead;
shead = (ListNode*)malloc(sizeof(ListNode));
shead->next = head;

// 创建一个指针以遍历链表
ListNode* cur = shead;

// 遍历链表
while (cur->next != NULL) {
// 如果下一个节点的值为 'val',将其从链表中删除
if (cur->next->val == val) {
ListNode* tmp = cur->next;
cur->next = cur->next->next;
free(tmp);
}
// 否则,将指针移至下一个节点
else {
cur = cur->next;
}
}

// 将原始链表的头指针指向更新后的头节点
head = shead->next;

// 释放伪头节点的内存
free(shead);

// 返回更新后的链表头指针
return head;
}

设计链表实现5个功能

typedef struct MyLinkedList {
int val; // 存储节点的值
struct MyLinkedList* next; // 指向下一个节点的指针
} MyLinkedList;

/** Initialize your data structure here. */
MyLinkedList* myLinkedListCreate() {
// 创建一个空的链表头节点
MyLinkedList* head = (MyLinkedList*)malloc(sizeof(MyLinkedList));
head->next = NULL; // 初始时链表为空,头节点的next指针指向NULL
return head;
}

/** Get the value of the index-th node in the linked list. If the index is invalid, return -1. */
int myLinkedListGet(MyLinkedList* obj, int index) {
// 从头节点开始遍历链表,找到第index个节点并返回其值
MyLinkedList* cur = obj->next; // cur指向第一个节点
for (int i = 0; cur != NULL; i++) {
if (i == index) {
return cur->val; // 找到第index个节点的值并返回
}
else {
cur = cur->next; // 继续遍历下一个节点
}
}
// 若index无效,返回-1
return -1;
}

/** Add a node of value val before the first element of the linked list. After the insertion, the new node will be the first node of the linked list. */
void myLinkedListAddAtHead(MyLinkedList* obj, int val) {
// 在头节点后插入新节点,使新节点成为链表的第一个节点
MyLinkedList* nhead = (MyLinkedList*)malloc(sizeof(MyLinkedList));
nhead->val = val;
nhead->next = obj->next; // 新节点指向原来的第一个节点
obj->next = nhead; // 头节点指向新节点
}

/** Append a node of value val to the last element of the linked list. */
void myLinkedListAddAtTail(MyLinkedList* obj, int val) {
// 从头节点开始遍历链表,找到最后一个节点,将新节点插入到其后面
MyLinkedList* cur = obj;
while (cur->next != NULL) {
cur = cur->next; // 找到最后一个节点
}
MyLinkedList* ntail = (MyLinkedList*)malloc(sizeof(MyLinkedList));
ntail->val = val;
ntail->next = NULL; // 新节点成为最后一个节点,其next指针指向NULL
cur->next = ntail; // 原来的最后一个节点指向新节点
}

/** Add a node of value val before the index-th node in the linked list. If index equals to the length of linked list, the node will be appended to the end of linked list. If index is greater than the length, the node will not be inserted. */
void myLinkedListAddAtIndex(MyLinkedList* obj, int index, int val) {
// 若index为0,则调用myLinkedListAddAtHead在头部插入新节点
if (index == 0) {
myLinkedListAddAtHead(obj, val);
return;
}
// 从头节点开始遍历链表,找到第index个节点的前一个节点,将新节点插入到其后面
MyLinkedList* cur = obj->next;
for (int i = 1; cur != NULL; i++) {
if (i == index) {
MyLinkedList* newnode = (MyLinkedList*)malloc(sizeof(MyLinkedList));
newnode->val = val;
newnode->next = cur->next; // 新节点指向原来index位置的节点
cur->next = newnode; // 前一个节点指向新节点
return;
}
else {
cur = cur->next; // 继续遍历下一个节点
}
}
}

/** Delete the index-th node in the linked list, if the index is valid. */
void myLinkedListDeleteAtIndex(MyLinkedList* obj, int index) {
// 若index为0,则直接删除头节点后的第一个节点
if (index == 0) {
MyLinkedList* tmp = obj->next;
if (tmp != NULL) {
obj->next = tmp->next; // 头节点指向被删除节点的下一个节点
free(tmp); // 释放被删除节点的内存
}
return;
}
// 从头节点开始遍历链表,找到第index个节点的前一个节点,将其指向下下个节点,删除第index个节点
MyLinkedList* cur = obj->next;
for (int i = 1; cur != NULL && cur->next != NULL; i++) {
if (i == index) {
MyLinkedList* tmp = cur->next;
if (tmp != NULL) {
cur->next = tmp->next; // 前一个节点指向被删除节点的下一个节点
free(tmp); // 释放被删除节点的内存
}
return;
}
else {
cur = cur->next; // 继续遍历下一个节点
}
}
}

void myLinkedListFree(MyLinkedList* obj) {
// 释放链表的所有节点内存
while (obj != NULL) {
MyLinkedList* tmp = obj;
obj = obj->next;
free(tmp);
}
}

/**
 * Your MyLinkedList struct will be instantiated and called as such:
 * MyLinkedList* obj = myLinkedListCreate();
 * int param_1 = myLinkedListGet(obj, index);
 * myLinkedListAddAtHead(obj, val);
 * myLinkedListAddAtTail(obj, val);
 * myLinkedListAddAtIndex(obj, index, val);
 * myLinkedListDeleteAtIndex(obj, index);
 * myLinkedListFree(obj);
*/

反转链表

双指针法

struct ListNode* reverseList(struct ListNode* head) {
// 保存cur的下一个结点
struct ListNode* temp;
// pre指针指向当前结点的前一个结点
struct ListNode* pre = NULL;
// 使用头插法反转链表
while (head) {
// 保存下一个结点的位置
temp = head->next;
// 将当前结点的 next 指针指向前一个结点,实现翻转操作
head->next = pre;
// 更新 pre 和 head 指针
pre = head;
head = temp;
}
// 返回反转后的链表的头节点
return pre;
}

递归法

#include <stdio.h>
#include <stdlib.h>

struct ListNode {
int val;
struct ListNode* next;
};

struct ListNode* reverse(struct ListNode* pre, struct ListNode* cur) {
// 如果当前节点为空,说明反转已经完成,返回前一个节点作为翻转后链表的头节点
if (!cur)
return pre;

// 保存当前节点的下一个节点
struct ListNode* temp = cur->next;

// 将当前节点的 next 指针指向前一个节点,实现翻转操作
cur->next = pre;

// 将当前节点作为前一个节点传入下一层递归
// 将 temp(原当前节点的下一个节点)作为当前节点传入下一层递归,
// 使其指针指向当前节点
return reverse(cur, temp);
}

struct ListNode* reverseList(struct ListNode* head) {
// 调用 reverse 函数,初始时前一个节点为 NULL,当前节点为 head
return reverse(NULL, head);
}

int main() {
// 创建一个示例链表 1->2->3->4->5
struct ListNode* head = malloc(sizeof(struct ListNode));
struct ListNode* node1 = malloc(sizeof(struct ListNode));
struct ListNode* node2 = malloc(sizeof(struct ListNode));
struct ListNode* node3 = malloc(sizeof(struct ListNode));
struct ListNode* node4 = malloc(sizeof(struct ListNode));

head->val = 1;
head->next = node1;
node1->val = 2;
node1->next = node2;
node2->val = 3;
node2->next = node3;
node3->val = 4;
node3->next = node4;
node4->val = 5;
node4->next = NULL;

// 调用反转函数
struct ListNode* reversedHead = reverseList(head);

// 打印反转后的链表
struct ListNode* current = reversedHead;
while (current != NULL) {
printf("%d ", current->val);
current = current->next;
}

// 释放链表内存
while (reversedHead != NULL) {
struct ListNode* temp = reversedHead;
reversedHead = reversedHead->next;
free(temp);
}

return 0;
}

两两交换链表中的节点

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



递归版本

struct ListNode* swapPairs(struct ListNode* head){
//递归结束条件:头节点不存在或头节点的下一个节点不存在。此时不需要交换,直接返回head
if(!head || !head->next)
return head;
//创建一个节点指针类型保存头结点下一个节点
struct ListNode *newHead = head->next;
//更改头结点+2位节点后的值,并将头结点的next指针指向这个更改过的list
head->next = swapPairs(newHead->next);
//将新的头结点的next指针指向老的头节点
newHead->next = head;
return newHead;
}

这段代码使用递归方式实现链表中相邻节点的交换。swapPairs 函数接受链表的头节点作为输入,并返回交换后的链表的头节点。

首先,我们设置递归的结束条件:当头节点不存在或头节点的下一个节点不存在时,直接返回头节点 head。这是因为在这种情况下,不需要进行交换操作。

然后,我们创建一个节点指针 newHead 来保存头节点的下一个节点。这将成为交换后的新头节点。

接下来,我们递归地调用 swapPairs 函数来交换剩余部分的节点。具体步骤如下:

  1. 将头节点的 next 指针指向递归调用返回的新头节点。
  2. 将新的头节点的 next 指针指向老的头节点。
  3. 最后,我们返回新头节点 newHead,这是交换后的链表的头节点。

双指针迭代版本

   struct ListNode* swapPairs(struct ListNode* head){
// 创建一个虚拟头节点,存储结果链表
typedef struct ListNode ListNode;
ListNode *fakehead = (ListNode *)malloc(sizeof(ListNode));
fakehead->next = head;

// 使用双指针进行迭代,避免使用中间变量
ListNode* right = fakehead->next;  // 右节点指针
ListNode* left = fakehead;  // 左节点指针

while(left && right && right->next ){
// 交换相邻节点的指针
// 将左节点的 next 指针指向右节点的下一个节点
left->next = right->next;

// 将右节点的下一个节点指针指向左节点的下一个节点的下一个节点
right->next = left->next->next;

// 将左节点的下一个节点的下一个节点指针指向右节点
left->next->next = right;

// 更新左右节点的位置
left = right;
right = left->next;
}

return fakehead->next;  // 返回结果链表
}

这段代码使用迭代方式实现链表中相邻节点的交换。swapPairs 函数接受链表的头节点作为输入,并返回交换后的链表的头节点。

首先,我们创建一个虚拟头节点 fakehead,并将其 next 指针指向传入的头节点 head。

然后,我们使用两个指针 left 和 right 进行迭代。left 指针表示相邻节点的前一个节点,right 指针表示相邻节点的后一个节点。

在循环中,我们判断 left、right 和 right->next 是否都存在。这是因为交换的条件是至少有两个相邻节点存在。

接下来,我们交换相邻节点的指针。具体步骤如下:

    • 将 left 的 next 指针指向 right 的下一个节点。
    • 将 right 的 next 指针指向 left 的下一个节点的下一个节点。
    • 将 left 的下一个节点的 next 指针指向 right。
    • 最后,我们更新 left 和 right 的位置,以继续迭代到下一对相邻节点。
    • 最终,我们返回虚拟头节点的 next 指针,这是交换后的链表的头节点。

删除链表的倒数第n个节点

struct ListNode* removeNthFromEnd(struct ListNode* head, int n) {
//定义虚拟头节点dummy 并初始化使其指向head
struct ListNode* dummy = malloc(sizeof(struct ListNode));
dummy->val = 0;
dummy->next = head;
//定义 fast slow 双指针
struct ListNode* fast = head;
struct ListNode* slow = dummy;

for (int i = 0; i < n; ++i) {
fast = fast->next;
}
while (fast) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;//删除倒数第n个节点
head = dummy->next;
free(dummy);//删除虚拟节点dummy
return head;
}

思路

双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。

思路是这样的,但要注意一些细节。

分为如下几步:

  1. 首先这里我推荐大家使用虚拟头结点,这样方便处理删除实际头结点的逻辑,定义fast指针和slow指针,初始值为虚拟头结点,如图:
  2. fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
  3. fast和slow同时移动,直到fast指向末尾,如题:
  4. 删除slow指向的下一个节点,如图:

链表相交

思路
简单来说,就是求两个链表交点节点的指针。 这里同学们要注意,交点不是数值相等,而是指针相等

为了方便举例,假设节点元素数值相等,则节点指针相等。

看如下两个链表,目前curA指向链表A的头结点,curB指向链表B的头结点:

我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:

此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

否则循环退出返回空指针。
#include <stdio.h>
#include <stdlib.h>

// 定义链表节点的结构体
struct ListNode {
int val;
struct ListNode* next;
};

// 寻找两个链表的交点
// 寻找两个链表的交点
struct ListNode* getIntersectionNode(struct ListNode* headA, struct ListNode* headB) {
struct ListNode* l = NULL, * s = NULL; // l指向较长的链表,s指向较短的链表
int lenA = 0, lenB = 0, gap = 0; // lenA和lenB分别表示两个链表的长度,gap表示两个链表长度差

// 求出链表A的长度
s = headA;
while (s) {
lenA++; // 遍历链表A,统计长度
s = s->next;
}
// 求出链表B的长度
s = headB;
while (s) {
lenB++; // 遍历链表B,统计长度
s = s->next;
}

// 求出两个链表长度差
if (lenA > lenB) {
l = headA, s = headB; // 如果链表A比链表B长,l指向A,s指向B
gap = lenA - lenB; // 计算长度差
}
else {
l = headB, s = headA; // 如果链表B比链表A长,l指向B,s指向A
gap = lenB - lenA; // 计算长度差
}

// 尾部对齐
while (gap--) {
l = l->next; // l指针向前移动,使得l和s指向相交节点的位置
}

// 移动,并检查是否有相同的元素
while (l) {
if (l == s) {
return l; // 如果找到相同的节点,则返回该节点
}
l = l->next; // l指针后移
s = s->next; // s指针后移
}

// 如果最后都没有找到相同的节点,则返回NULL,表示两个链表没有相交节点
return NULL;
}

// 主函数
int main() {
// 构造两个链表
struct ListNode* commonNode = (struct ListNode*)malloc(sizeof(struct ListNode));
commonNode->val = 8;
commonNode->next = (struct ListNode*)malloc(sizeof(struct ListNode));
commonNode->next->val = 4;
commonNode->next->next = (struct ListNode*)malloc(sizeof(struct ListNode));
commonNode->next->next->val = 5;
commonNode->next->next->next = NULL;

struct ListNode* headA = (struct ListNode*)malloc(sizeof(struct ListNode));
headA->val = 4;
headA->next = (struct ListNode*)malloc(sizeof(struct ListNode));
headA->next->val = 1;
headA->next->next = commonNode;

struct ListNode* headB = (struct ListNode*)malloc(sizeof(struct ListNode));
headB->val = 5;
headB->next = (struct ListNode*)malloc(sizeof(struct ListNode));
headB->next->val = 6;
headB->next->next = (struct ListNode*)malloc(sizeof(struct ListNode));
headB->next->next->val = 1;
headB->next->next->next = commonNode;

// 调用getIntersectionNode函数
struct ListNode* intersectionNode = getIntersectionNode(headA, headB);

// 输出交点信息
if (intersectionNode) {
printf("两个链表相交的节点值为:%d\n", intersectionNode->val);
}
else {
printf("两个链表不相交\n");
}

// 释放内存
free(headA);
free(headB);
free(commonNode);
return 0;
}

环形链表2

题都没看懂。。。。

posted on 2024-01-28 19:35  lulixiu  阅读(3)  评论(0编辑  收藏  举报