环形链表
https://leetcode-cn.com/problems/linked-list-cycle/
思路:双指针法。定义快指针q一次走两个,慢指针p一次走一个,当两个相遇则有环(类似于在环形跑道赛跑,总有一刻慢的人和快的人相遇)
/**
* 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==NULL){
return false;
}
ListNode *p = head;//慢指针
ListNode *q = head;//快指针
do
{
if(q==NULL||q->next==NULL)
return false;
p = p->next;
q = q->next->next;
}while (p!=q);
return true;
}
};
环形链表 II
https://leetcode-cn.com/problems/linked-list-cycle-ii/
思路:
因此,让一个慢指针从头部开始走,快指针从相遇点走,两个指针的步长都为1,当这两个指针相遇时就走了dis步此时就是入环点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==NULL){
return nullptr;
}
ListNode *f = head;
ListNode *l = head;
do{
if(!f || !f->next)
return nullptr;
f = f->next->next;
l = l->next;
}while(f!=l);
ListNode *temp = f;
int count = 0;
do{
temp = temp->next;
count++;
}while(temp!=f);
f = head;
l = head;
for(int i = 0; i<count; i++){
f = f->next;
}
while(f!=l){
f = f->next;
l = l->next;
}
return f;
}
};
相交链表
https://leetcode-cn.com/problems/intersection-of-two-linked-lists/
思路:由题目可知,如果两条链表相交,那么从相交点之后的链表长度是相等的,因此,只要找到两条链表中长的那一条,分别用两个指针指向两条链表,让两个指针从距离末尾相同距离的位置(只要找到两条链表的长度差即可)开始遍历。
/**
* 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) {
ListNode *p = headA;
ListNode *q = headB;
while(p!=nullptr&& q!= nullptr){
p = p->next;
q = q->next;
}
int count = 0,flag = 0;
while(q!=nullptr){//headB比较长
q = q->next;
count++;
}
while(p!=nullptr){//headA比较长
flag = 1;
p = p->next;
count++;
}
p = headA;
q = headB;
if(flag){
for(int i = 0; i<count;++i){
p = p->next;
}
while(p!=q){
p = p->next;
q = q->next;
}
}else{
for(int i = 0; i<count;++i){
q = q->next;
}
while(q!=p){
p = p->next;
q = q->next;
}
}
return p;
}
};
删除链表的倒数第N个节点
https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/
思路:双指针法。首先让p指针先移动n步,之后p,q指针共同移动直到p指针到达尾部为止,因为p和q二者的距离为n,因此当p到达尾部时,q的位置恰好是倒数第n个节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
if(head==NULL){
return head;
}
ListNode *p = head;
ListNode *q = head;
while(n--){
p = p->next;
}
if(p==NULL){
return head->next;
}
while(p->next!=NULL){
p = p->next;
q = q->next;
}
q->next = q->next->next;
return head;
}
};
反转链表
https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/
递归法:一直递归到链表的最后一个结点,该结点就是反转后的头结点,记作 head.每次函数的返回值,让当前结点的下一个结点的 next指针指向当前节点。
同时让当前结点的 next指针指向 nullptr ,从而实现反转
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr||head->next==nullptr){
return head;
}
ListNode *p = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return p;
}
};
迭代法:因为链表的头结点就是反转链表的尾结点,定义cur,刚开始指向头结点。每次迭代都让头指针head的 下一个结点的next指向cur ,实现反转,然后cur 和 head的 next指针同时 往后移动一个位置,直到cur到达链表的尾节点
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head == nullptr){
return head;
}
ListNode *cur = head;
while(head->next!=nullptr){
ListNode *temp = head->next->next;
head->next->next = cur;
cur= head->next;
head->next = temp;
}
return cur;
}
};
移除链表元素
https://leetcode-cn.com/problems/remove-linked-list-elements/
思路:本题要求删除链表中元素,只需要让待删除节点之前一个节点指向待删除节点之后一个节点即可。需要注意的是:因为本题的返回值是头结点,所以首先要判断头结点是不是要删除的节点,可以采用循环操作判断第一个不是要删除的节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head == nullptr){
return head;
}
while(head!=nullptr && head->val == val){//用while的原因避免出现多个重复,如[1,1]
head = head->next;
}
ListNode *p = head;
if(p!=nullptr){
while(p->next!=nullptr){
if(p->next->val== val){
p->next = p->next->next;
}
else{
p = p->next;
}
}
}
return head;
}
};
奇偶链表
https://leetcode-cn.com/problems/odd-even-linked-list/
思路:本题要将偶链表接到奇链表的尾部,因此首先定义奇偶链表各自的头尾指针,奇链表的头指针为head,尾指针为odd;偶链表的头指针为val,尾指针为even,遍历原链表完成奇偶链表各自的赋值,最后将两个链表拼接起来,将奇链表的尾节点的next指针指向偶链表的头结点即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
if(head==NULL||head->next==NULL){
return head;
}
ListNode *odd = head;
ListNode *even = head->next;
ListNode *val = even;
while(even!=NULL&&even->next!=NULL){
odd->next = even->next;
odd = odd->next;
even->next = odd->next;
even = even->next;
}
odd->next = val;
return head;
}
};
回文链表
https://leetcode-cn.com/problems/palindrome-linked-list/
思路:本题可以想到的是用两个指针分别指向链表首尾两端,然后向链表中间进行移动并判断。所以,用快慢指针遍历(快的每次走两步,慢的每次走一步,一次遍历找到链表的中点),对链表中间往后的部分进行链表的反转操作,与前半部分进行值的比较,看看是否是回文序列
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head==nullptr||head->next==nullptr){
return true;
}
ListNode *p = head;
ListNode *q = head;
while(q!=nullptr&&q->next!=nullptr){
q = q->next->next;//快指针
p = p->next;//慢指针
}
ListNode *pre = nullptr;
while(p!=nullptr){
ListNode *next = p->next;
p->next = pre;
pre = p;
p = next;
}
while(head!=nullptr&&pre!=nullptr){
if(head->val!=pre->val){
return false;
}
head = head->next;
pre = pre->next;
}
return true;
}
};
合并两个有序链表
https://leetcode-cn.com/problems/merge-two-sorted-lists/
思路:使用一个哨兵节点pHead,使用 pHead->next 来保存需要返回的头节点,逐个遍历比较数组中的元素,找到小的那一个接到链表的尾部,直到有一条链表为 nullptr ,即可将另一条链表剩余的节点直接都接在所求链表的尾部
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2){
ListNode *pHead = new ListNode(0);
ListNode *temp = pHead;
ListNode *p = l1;
ListNode *q = l2;
while(p!=nullptr&&q!=nullptr){
if(p->val<q->val){
temp->next = p;
p = p->next;
temp = temp->next;
}else{
temp->next = q;
q = q->next;
temp = temp->next;
}
}
while(p!=nullptr){
temp->next = p;
p = p->next;
temp = temp->next;
}
while(q!=nullptr){
temp->next = q;
q= q->next;
temp = temp->next;
}
return pHead->next;
}
};
两数相加
https://leetcode-cn.com/problems/add-two-numbers/
思路:将两个链表看成相同长度,遍历过程中如果一个链表较短则在前面补 0,每一位计算要考虑上一位的进位问题,计算结束后同样要更新进位值,注意:当两个链表全部遍历完毕后,进位值为 1,则在新链表最前方添加值为 1的节点
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *dummy = new ListNode(0);
ListNode *p = dummy;
int carry = 0;
while(l1|| l2 || carry){
int pVal = l1 ?l1->val:0;
int qVal = l2 ?l2->val:0;
//每一位进行相加减
int sum = pVal + qVal + carry;
carry = sum/10;
sum = sum%10;
//这样做的目的是检查是否都等于空的情况
ListNode *next = l1?l1:l2;
//如果l1和l2都为空,说明已经是剩下进位的情况了
if(next==nullptr){
next = new ListNode(sum);
}
next->val = sum;
p->next = next;
p = p->next;
//进一步排除存在其中之一为空或两者都为空的现象
l1 = l1?l1->next:nullptr;
l2 = l2?l2->next:nullptr;
}
return dummy->next;
}
};
旋转链表
https://leetcode-cn.com/problems/rotate-list/
思路:找到链表的尾部并将其与头部相连 使得整个链表成环,并统计链表的长度 count。对于给定的k值,由count - k%count确定最后需要移动的步长,从该处断开即可。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head==nullptr||head->next==nullptr||k==0){
return head;
}
/*更为简单的一种写法,先连成环,就不用考虑多种特殊情况*/
ListNode *tail = head;
int count=1;
while(tail->next!=nullptr){
count++;
tail = tail->next;
}
//加这个条件能够使得效率一下子提高很多
int pos = k%count;
tail->next = head;
if(pos){
for(int i= 0;i<count-pos; i++){
tail = tail->next;
}
}
ListNode *val= tail->next;
tail->next=nullptr;
return val;
};