C友们,如果觉得光看文字不好理解,我在b站上发了关于链表题目的相关视频,欢迎各位前来:链表题目视频
2022/7/6
141. 环形链表
题目:
给你一个链表的头节点 head ,判断链表中是否有环。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
注意:pos 不作为参数进行传递 。仅仅是为了标识链表的实际情况。
如果链表中存在环 ,则返回 true 。 否则,返回 false 。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
解题思路为:使用双指针法
本方法需要读者对「Floyd 判圈算法」(又称龟兔赛跑算法)有所了解。
假想「乌龟」和「兔子」在链表上移动,「兔子」跑得快,「乌龟」跑得慢。当「乌龟」和「兔子」从链表上的同一个节点开始移动时,如果该链表中没有环,那么「兔子」将一直处于「乌龟」的前方;如果该链表中有环,那么「兔子」会先于「乌龟」进入环,并且一直在环内移动。等到「乌龟」进入环时,由于「兔子」的速度快,它一定会在某个时刻与乌龟相遇,即套了「乌龟」若干圈。
我们可以根据上述思路来解决本题。具体地,我们定义两个指针,一快一满。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针在位置 head,而快指针在位置 head.next。这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。否则快指针将到达链表尾部,该链表不为环形链表。
当块指针等于null或fast->next等于null时,跳出循环。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==nullptr||head->next==nullptr){
return false;
}
ListNode *slow=head;
ListNode *fast=head->next;
while(slow!=fast){
if(fast==nullptr||fast->next==nullptr)
{return false; }
slow=slow->next;
fast=fast->next->next;
}
return true;
}
};
2022/7/8
142. 环形链表 II
给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。
为了表示给定链表中的环,评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。
如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:返回索引为 0 的链表节点
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:返回 null
解释:链表中没有环。
解题思路为:
方法一:哈希表
一个非常直观的思路是:我们遍历链表中的每个节点,并将它记录下来;一旦遇到了此前遍历过的节点,就可以判定链表中存在环。借助哈希表可以很方便地实现。
代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};
时间复杂度为:O(n),其中n 为链表中节点的数目。我们恰好需要访问链表中的每一个节点。
空间复杂度为:O(n),其中 n 为链表中节点的数目。我们需要将链表中的每个节点都保存在哈希表当中。
方法二:快慢指针
思路与算法
我们使用两个指针,fast 与 slow。它们起始都位于链表的头部。随后,slow 指针每次向后移动一个位置,而 fast 指针向后移动两个位置。如果链表中存在环,则 fast 指针最终将再次与slow 指针在环中相遇。
因此,当发现 slow 与 fast 相遇时,我们再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和slow 每次向后移动一个位置。最终,它们会在入环点相遇。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow = head, *fast = head;
while (fast != nullptr) {
slow = slow->next;
if (fast->next == nullptr) {
return nullptr;
}
fast = fast->next->next;
if (fast == slow) {
ListNode *ptr = head;
while (ptr != slow) {
ptr = ptr->next;
slow = slow->next;
}
return ptr;
}
}
return nullptr;
}
};
时间复杂度:O(n),其中 n 为链表中节点的数目。在最初判断快慢指针是否相遇时,slow 指针走过的距离不会超过链表的总长度;随后寻找入环点时,走过的距离也不会超过链表的总长度。因此,总的执行时间为 O(N)+O(N)=O(N)。
空间复杂度:O(1),我们只使用了slow,fast,ptr 三个指针
2022/7/10
160. 相交链表
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null 。
图示两个链表在节点 c1 开始相交:
题目数据 保证 整个链式结构中不存在环。
注意,函数返回结果后,链表必须 保持其原始结构 。
自定义评测:
评测系统 的输入如下(你设计的程序 不适用 此输入):
intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
listA - 第一个链表
listB - 第二个链表
skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数
评测系统将根据这些输入创建链式数据结构,并将两个头节点 headA 和 headB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案 。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at ‘8’
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。
在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
示例 2:
输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1
输出:Intersected at ‘2’
解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。
从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。
在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。
示例 3:
输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2
输出:null
解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。
由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。
这两个链表不相交,因此返回 null 。
解题思路:
首先,创建一个哈希集合,将链表A的所有元素都放入到该哈希集合中,然后判断该哈希集合中是否存在链表B的元素值,若含有,则返回该节点,若把整个链表B都遍历完后都不存在与哈希集合中相等的节点,则返回false。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> visited;
ListNode * temp=headA;
while(temp!=nullptr){
visited.insert(temp);
temp=temp->next;
}
temp=headB;
while(temp!=nullptr){
if(visited.count(temp)){
return temp;
}
else
{
temp=temp->next;
}
}
return nullptr;
}
};
时间复杂度为:O(m+n),m、n为链表A,链表B的长度,分别遍历链表A和链表B.
空间复杂度:m为链表A的全部节点,需要用哈希集合存储链表A的全部节点。
方法二:
首先创建2个指针pA,pB分别指向两个链表,pa指向链表A的头结点,pb指向链表B的头结点,当指针pA遍历完链表A后,让pA指向链表B的头结点继续遍历链表B.同理,当指针pB遍历完链表B后,让pB指向链表A的头结点继续遍历链表A.当pA,pB相遇时指向的节点即为两个链表的交点。
代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr||headB==nullptr)
return nullptr;
ListNode * pa;
ListNode * pb;
pa=headA;
pb=headB;
while(pa!=pb){
if(pa==nullptr)
{
pa=headB;
}
else
pa=pa->next;
if(pb==nullptr){
pb=headA;
}
else
pb=pb->next;
}
return pa;
}
};
时间复杂度为:O(m+n),m、n为链表A,链表B的长度,分别遍历链表A和链表B.每个指针遍历两个链表各一次。
空间复杂度为:O(1)
2022/7/12
19. 删除链表的倒数第 N 个结点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
示例 1:
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
示例 2:
输入:head = [1], n = 1
输出:[]
示例 3:
输入:head = [1,2], n = 1
输出:[1]
解题思路为:双指针法
哑结点其实就是放在第一个存放数据结点之前、头结点之后的结点。加入哑结点之后就可以使所有数据结点都有前驱结点,这样就会方便执行链表的一些操作。
首先,创建一个哑结点链接在头结点的前面,让哑结点成为第一个结点,创建2个指针分别为快指针和慢指针,让快指针指向头结点,让慢指针指向哑结点。因为要找到倒数第n个结点,所以让快指针先移动n个单位,当快指针所指向的位置与慢指针所指向位置相差n,让快指针与慢指针同时移动,当快指针指向空时,慢指针即指向了将要删除的节点的前一个结点,让其next指针指向下下一个节点,即删除了倒数第n个节点。
最后删除哑结点,返回刚开始的头结点。
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dull=new ListNode(0,head);
ListNode *slow=dull;
ListNode *fast=head;
int count=0;
for(int i=0;i<n;i++){
fast=fast->next;
}
while(fast!=nullptr){
fast=fast->next;
slow=slow->next;
}
slow->next=slow->next->next;
ListNode *ptr=dull->next;
delete dull;
return ptr;
}
};
时间复杂度:O(L),L是链表的长度。
空间复杂度:O(1)
方法二:计算链表长度
我们首先从头节点开始对链表进行一次遍历,得到链表的长度 L。随后我们再从头节点开始对链表进行一次遍历,当遍历到第 L-n+1 个节点时,它就是我们需要删除的节点。
为了方便删除操作,我们可以从哑节点开始遍历 L-n+1个节点。当遍历到第 L-n+1 个节点时,它的下一个节点就是我们需要删除的节点,这样我们只需要修改一次指针,就能完成删除操作。
class Solution {
public:
int getLength(ListNode* head) {
int length = 0;
while (head) {
++length;
head = head->next;
}
return length;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
int length = getLength(head);
ListNode* cur = dummy;
for (int i = 1; i < length - n + 1; ++i) {
cur = cur->next;
}
cur->next = cur->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
时间复杂度:O(L),L是链表的长度。
空间复杂度:O(1)
方法三:栈
我们也可以在遍历链表的同时将所有节点依次入栈。根据栈「先进后出」的原则,我们弹出栈的第 nn 个节点就是需要删除的节点,并且目前栈顶的节点就是待删除节点的前驱节点。这样一来,删除操作就变得十分方便了。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummy = new ListNode(0, head);
stack<ListNode*> stk;
ListNode* cur = dummy;
while (cur) {
stk.push(cur);
cur = cur->next;
}
for (int i = 0; i < n; ++i) {
stk.pop();
}
ListNode* prev = stk.top();
prev->next = prev->next->next;
ListNode* ans = dummy->next;
delete dummy;
return ans;
}
};
时间复杂度:O(L),其中 L是链表的长度。
空间复杂度:O(L),其中 L 是链表的长度。主要为栈的开销。
2022/7/14
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
输出:[]
解题思路:
首先设置一个哑结点指向头结点的,哑结点是为了防止当头结点被删除后,链表为空。创建一个临时指针指向哑结点,让临时指针遍历整个链表,我们找到符合条件的节点的前驱节点,让这个前驱节点指向满足条件节点的后一个节点,即把满足条件的结点删除了。当遍历完这个链表后,也完成了相应的操作。
时间复杂度为:O(n),n为链表的长度,需要遍历链表一次。
空间复杂度为:O(1)
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode * dull=new ListNode(0,head);
ListNode *pa=dull;
while(pa->next!=nullptr)
{
if(pa->next->val==val)
pa->next=pa->next->next;
else
pa=pa->next;
}
ListNode *ptr=dull->next;
delete dull;
return ptr;
}
};
法2:使用递归算法:c语言:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* removeElements(struct ListNode* head, int val){
if(head==NULL)
{
return head;
}
head->next=removeElements(head->next,val);
if(head->val==val)
{
struct ListNode* tmp = head->next;
free(head);
return tmp;
}
return head;
}
时间复杂度:O(n),其中 n 是链表的长度。递归过程中需要遍历链表一次。
空间复杂度:O(n),其中 n 是链表的长度。空间复杂度主要取决于递归调用栈,最多不会超过 n 层。
2022/7/17
234. 回文链表
给你一个单链表的头节点 head ,请你判断该链表是否为回文链表。如果是,返回 true ;否则,返回 false 。
示例 1:
输入:head = [1,2,2,1]
输出:true
示例 2:
输入:head = [1,2]
输出:false
解题思路:
首先遍历一边链表当中的元素,将链表中的数据放入到新创建的数组当中,设置双指针,用来判断是否为回文串。用双指针来判断数组是否为回文串的方法为
设置左右指针分别指向字符串的首端和末端,依次比较所指向的数字是否相同?若相同则left++,right–,直至left==right时,则遍历结束。
注意:用到的API为isalnum()函数用来检测一个字符是否是字母或十进制数字。
返回值非0表示字符为字母或十进制数字,返回值为0表示字符为非字母或非十进制数字。
时间复杂度:O(n),n为链表的长度,也就是链表中元素的个数。第一步: 遍历链表并将值复制到数组中。
空间复杂度:O(n),n为链表的元素个数,我们使用了一个数组对链表元素进行了存放。
代码:
class Solution {
public:
bool isPalindrome(string s) {
int left=0;
string tea;
for(char c:s){
if(isalnum(c)){
tea+=tolower(c);
}
}
int right=tea.size()-1;
while(left<right){
if(tea[left]==tea[right]){
left++;
right--;
}
else
{
return false;
break;
}
}
return true;
}
};
2022/7/18
21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
示例 1:
输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]
示例 2:
输入:l1 = [], l2 = []
输出:[]
示例 3:
输入:l1 = [], l2 = [0]
输出:[0]
解题思路:
将两个有序链表进行连接,将两个链表进行连接起来,不占用新的空间,常见一个新的头结点prevhead,定义一个新的指针prev指向prevhead,当两个链表均不为空时,对节点进行遍历,当list1->val<list2->val
时,让prev指向list1,list1右移,当list2->val<list1->val
时,让prev指向list2,list2右移,让prev=prev->next
让prev向右移动,最后prev->next=list1==nullptr?list2:list1;
list1是否为空,若是,则prev->next=list2,若不是空,则prev->next=list1.
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode * prevhead=new ListNode(-1);
ListNode * prev=prevhead;
while(list1!=nullptr&&list2!=nullptr)
{
if(list1->val<list2->val)
{
prev->next=list1;
list1=list1->next;
}
else
{
prev->next=list2;
list2=list2->next;
}
prev=prev->next;
}
prev->next=list1==nullptr?list2:list1;
return prevhead->next;
}
};
2022/7/20
2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
解题思路为:
这个题是将两个链表的节点的val值分别相加得到数放到一个新生成的链表当中。当2个链表的长度相同时,分别将2个链表的节点依次相加放入到新生成的链表的节点当中,当最后尾节点相加时,若有进位则创建新节点将进位放入到其中。
当2个链表长度不同时,即一长一短时,可以将先遍历完的那个链表的值赋值为0,在后续的遍历过程中,其值设为了0,它的遍历也结束了。
在同时遍历2个链表的过程中,当其中一个链表为空时,不在继续遍历,遍历另一个链表即可,直到另一个链表为空时,跳出循环结束。
时间复杂度为:O(max(m,n)),m、n分别为2个链表的长度,我们需要遍历2个链表的全部位置,处理每个位置需要O(1)的时间。
空间复杂度为:O(1)
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *p1;
ListNode *p2;
ListNode * ptr=nullptr;
ListNode * head=nullptr;
p1=l1;
p2=l2;
int temp;
int carry=0;
int sum;
int n1,n2;
while(p1!=nullptr||p2!=nullptr){
if(p1==nullptr)
n1=0;
if(p1!=nullptr)
n1=p1->val;
if(p2==nullptr)
n2=0;
if(p2!=nullptr)
n2=p2->val;
temp= (n1+n2+carry)%10;
carry=(n1+n2+carry)/10;
if(head==nullptr)
{
ptr=head=new ListNode(temp);
}
else
{
ptr->next=new ListNode(temp);
ptr=ptr->next;
}
if(p1!=nullptr)
p1=p1->next;
if(p2!=nullptr)
p2=p2->next;
}
if(carry>0)
ptr->next=new ListNode(carry);
return head;
}
};
2022/7/21
61. 旋转链表
给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[4,5,1,2,3]
示例 2:
输入:head = [0,1,2], k = 4
输出:[2,0,1]
解题思路为
首先,我们可以通过遍历链表来获得链表的长度,将链表的最后一个节点与第一个节点相连接,形成一个有环的链表,旋转链表的实质是将2个节点之间的连接将其断开,形成一个新的头结点和一个尾节点。
例如,当旋转2次后新链表的头结点为4,尾节点为3,其实就是将3和4之间的连接断开了。将头结点设为val值为4的节点。我们从头结点遍历,找到值为3的节点后,将它与值为4的节点断开。
时间复杂度为:O(n),最坏的情况下,遍历链表2次。
空间复杂度为:O(1)
代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head==nullptr||k==0||head->next==nullptr)
return head;
ListNode *ptr =head;
int length=1;
while(ptr->next!=nullptr){
ptr=ptr->next;
length++;
}
ptr->next=head;
int m=k%length;
int count=length-m;
ListNode * ptr1=head;
int i=1;
while(i<count)
{
ptr1=ptr1->next;
i++;
}
ListNode *newhead =ptr1->next;
ptr1->next=nullptr;
return newhead;
}
};
2022/8/23
剑指 Offer 06. 从尾到头打印链表
栈的特点是后进先出,即最后压入栈的元素最先弹出。考虑到栈的这一特点,使用栈将链表元素顺序倒置。从链表的头节点开始,依次将每个节点压入栈内,然后依次弹出栈内的元素并存储到数组中。
创建一个栈,用于存储链表的节点
创建一个指针,初始时指向链表的头节点
当指针指向的元素非空时,重复下列操作:
将指针指向的节点压入栈内
将指针移到当前节点的下一个节点
获得栈的大小 size,创建一个数组 print,其大小为 size
创建下标并初始化 index = 0
重复 size 次下列操作:
从栈内弹出一个节点,将该节点的值存到 print[index]
将 index 的值加 1
返回 print
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
vector<int> reversePrint(ListNode* head) {
stack<ListNode*> stack;
vector<int> arr;
ListNode* ptr=head;
while(ptr!=nullptr)
{
stack.push(ptr);
ptr=ptr->next;
}
while(stack.empty()==false)
{
ListNode *node=stack.top();
arr.push_back(node->val);
stack.pop();
}
return arr;
}
};
206. 反转链表
创建三个指针分别为pre,temp,和after,temp指向当前节点,pre指向temp的上一个节点,after指向temp的下一个节点,首先让temp指向头结点,pre值为空,让after指针指向temp节点的下一个节点,让temp->next=pre;pre=temp;temp=after;最后返回节点pre.
c:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* reverseList(struct ListNode* head){
struct ListNode* pre;
struct ListNode* temp;
struct ListNode* after;
pre=NULL;
temp=head;
while(temp!=NULL)
{
after=temp->next;
temp->next=pre;
pre=temp;
temp=after;
}
return pre;
}
时间复杂度:O(n),其中n是链表的长度。需要遍历链表一次。
空间复杂度:O(1)。
2022/11/5
147. 对链表进行插入排序
c代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* insertionSortList(struct ListNode* head){
if(head==NULL)
return head;
struct ListNode* dummyHead=malloc(sizeof(struct ListNode));
dummyHead->val=0;
dummyHead->next=head;
struct ListNode* lastSorted=head;
struct ListNode* cur=head->next;
while(cur!=NULL)
{
if(lastSorted->val<=cur->val)
lastSorted=lastSorted->next;
else
{
struct ListNode* prev=dummyHead;
while(prev->next->val<cur->val)
prev=prev->next;
lastSorted->next=cur->next;
cur->next=prev->next;
prev->next=cur;
}
cur=lastSorted->next;
}
return dummyHead->next;
}
83. 删除排序链表中的重复元素
c:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* deleteDuplicates(struct ListNode* head){
if(head==NULL)
return head;
struct ListNode* ptr=head;
while(ptr->next!=NULL)
{
if(ptr->val==ptr->next->val)
ptr->next=ptr->next->next;
else
ptr=ptr->next;
}
return head;
}
2022/11/6
奇偶链表
将索引为奇数的元素连接成一个链表,将索引为偶数的元素连接成一个链表,将偶数链表连接到奇数链表的末尾。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* oddEvenList(struct ListNode* head){
if(head==NULL)
return head;
struct ListNode * odd=head;
struct ListNode * even=head->next;
struct ListNode *evenHead=head->next;
while(even!=NULL&&even->next!=NULL)
{
odd->next=even->next;
odd=even->next;
even->next=odd->next;
even=even->next;
}
odd->next=evenHead;
return head;
}
剑指 Offer 22. 链表中倒数第k个节点
解题思路:定义2个指针,ptr1和ptr2,让2个指针同时指向head节点,首先让ptr1先走k个位置后,让ptr2指向head节点,然后2个指针同时遍历整个链表,当ptr1遍历完整个链表后,ptr2所指向的位置就是链表的倒数第k个节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* ptr1=head;
struct ListNode* ptr2=head;
while(ptr1!=NULL)
{
ptr1=ptr1->next;
k--;
if(k<0)
{
ptr2=ptr2->next;
}
}
return ptr2;
}
法2:首先遍历整个链表得到链表的长度len,倒数第k个元素就是正数第len-k个元素,从头结点遍历找到正数第len-k个元素即可
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* getKthFromEnd(struct ListNode* head, int k){
struct ListNode* ptr=head;
int len=0;
while(ptr!=NULL)
{
ptr=ptr->next;
len++;
}
ptr=head;
for(int i=0;i<len-k;i++)
{
ptr=ptr->next;
}
return ptr;
}