LeetCode算法打卡--链表

61. 旋转链表

给定一个链表,旋转链表,将链表每个节点向右移动 个位置,其中 是非负数。

链接:https://leetcode-cn.com/problems/rotate-list/

/**
基本思想:先把单链表改成循环链表,然后头指针一直走loop的长度,最后改成单链表返回头
*/
/**
 * 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==NULL || k==0)
            return head;
        ListNode *h = head;   //头指针
        ListNode *f = NULL;   //尾指针
        int length = 1;
        while(h->next!=NULL)
        {
            length++;
            h = h->next;
        }
        int loop = length - (k%length);//循环次数
        f = h;
        h->next = head;
        h = head;
        for(int i=0;i<loop;i++)
        { 
           
            h = h->next;
            f = f->next;
           
        }
        f->next = NULL;  //改成单链表
        return h;
        
    }
};

24. 两两交换链表中的节点

给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

链接:https://leetcode-cn.com/problems/swap-nodes-in-pairs/

/*递归思想:按照正常思路交换两个节点为子问题,返回值连接前面的应该正确连接的节点
*/
/*非递归思想:设置三个指针,h指向要交换的第一个节点,f指向要交换的第二个节点,p指向第一个之前的一个节点
    交换的时候:p->next指向f
               h->next指向
               f->nextf->next指向h
              注意循环时,p,h,f的移动(指针是随着交换移动的,不是单纯的值的移动)
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*ListNode* swapPairs(ListNode* head) {
        if(head == NULL || head->next == NULL)
            return head;
        ListNode *next = head->next;
        head->next = swapPairs(next->next);
        next->next = head;
        
        return next;
    }*/
    
    ListNode* swapPairs(ListNode* head) {
        
        if(head == NULL || head->next == NULL)
            return head;
        ListNode *p =new ListNode(0);   //设置前面一个虚拟节点
        p->next = head;
        ListNode *h = head;
        ListNode *f = head->next;
        ListNode *newNode = f;
        
        while(h!=NULL)
        {
            p->next = f;
            h->next = f->next;
            f->next = h;
            p = h;
            h = h -> next; 
            if(h==NULL || h->next == NULL)   
                break;
            f = h->next;
        }
        return newNode;
    }
};

 

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

链接:https://leetcode-cn.com/problems/sort-list/

/*
   基本思想:归并排序,找中点,左右排序合并
            链表的中点用两个指针,一个每次走两步,一个每次走一步,快指针到达最后,则慢指针的位置就是中点
            左右分别归并,注意左边排序时,中点的next要设置为NULL
            左右排序之后合并
*/

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode * getMiddleList(ListNode* L)
    {
        
        ListNode *L1=L;
        ListNode *L2=L->next;
        
        while(L2!=NULL && L2->next != NULL)
        {
            
            L1 = L1->next;
            L2 = L2->next->next;
           
        }
        return L1;
    }
    ListNode* MergeList(ListNode*L1,ListNode* L2)
    {
        if(L1 == NULL)
            return L2;
        if(L2 == NULL)
            return L1;
        ListNode* result = new ListNode(0);
        ListNode* head = result;
        while(L1!=NULL && L2!=NULL){
        if(L1->val <= L2->val)
        {
            result->next = L1;
            L1 = L1->next;
            
        }
        else 
        {
            result->next = L2;
            L2 = L2->next;
        }
            result = result->next;
        }
        if(L1==NULL)
        {
             result->next = L2;
        }
        if(L2==NULL)
            result->next = L1;
        return head->next;
    }
    ListNode *sortList(ListNode *head) {
        if(head==NULL || head->next == NULL)
            return head;
        
        ListNode *middle = getMiddleList(head); 
        cout<<middle->val<<endl;
        ListNode *right = sortList(middle->next);
        
        middle->next = NULL; 
        ListNode *left = sortList(head);
        
        return MergeList(left,right);
    }
};

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

链接:https://leetcode-cn.com/problems/reverse-linked-list-ii/

/*
   基本思想:把要反转的部分截取出来,进行反转,然后再拼接
    注意,对于链表操作的时候,指向的链表的指针对链表的修改就是整个链表的变动
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseBetween(ListNode* head, int m, int n) {
        ListNode *ltail;
        ListNode *rhead;
        ListNode *dummyHead = new ListNode(0);
        ListNode *cur = dummyHead;
        ListNode *reversenode;   //保存要反转的部分的头
        dummyHead->next = head;
        int i=0;
        for(i=0;i<=n;i++)
        {
            if(i==m-1)
            {
                ltail = cur;   //前面保留的尾
                reversenode = cur->next;   //要反转的部分的头
            }
           
            if(i==n)
            {
               
                rhead = cur->next;   //后面要保留的部分的头
                cur->next = NULL;    
            }
            
             cur = cur->next;
        }

        ListNode* pre = NULL;
        cur =reversenode;
        while (cur != NULL) {       //进行反转
            ListNode* nextTemp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nextTemp;
        }
        
           ltail->next = pre;           //连接剩余不变的部分
           reversenode->next = rhead;
        return dummyHead->next;
    }
};

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

链接:https://leetcode-cn.com/problems/partition-list/

/*基本思想:先找到第一个大于或等于x的位置,从这个位置开始向后找小于x的,找到就遍历前面的第一个大于或等于x的位置,找到该位置进行链表分开和拼接
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* partition(ListNode* head, int x) {
       ListNode* cur;
        ListNode* dummpNode = new ListNode(0);
        dummpNode->next = head;
        ListNode* lhead = dummpNode;
        ListNode* list =  dummpNode->next;
        int min;
        if(head==NULL)
            return head;
        while(list!=NULL)
        {
            if(list->val >= x )
            {
                 cur = list;
                 break;
            }
               
            list = list->next;
        }

        
        ListNode *pre = cur;
        while(cur!=NULL)
        {
              
            cout<<"c"<<cur->val;
             
            if(cur->val <x){
              
              while(lhead!=NULL&&lhead->next!=NULL)
               {
                   cout<<"l"<<lhead->val;
                if(lhead->next->val >= x)
                {
                    ListNode* tmp = cur;
                    if(tmp!=NULL)
                    {
                      pre->next = cur->next;
                      tmp->next = lhead->next;
                      lhead->next = tmp;
                    }
                    lhead = lhead->next;
                    break;
                    
                } 
                    lhead = lhead->next;
               }
                
            }
        
            pre = cur;
            cur = cur->next;
        
            
        }
        return dummpNode->next;
    }
    
};



//另一种思路:维护两个链表,一个比x大,一个比x小最后两个链表拼接

 

2. 两数相加

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头


链接:https://leetcode-cn.com/problems/add-two-numbers


 

/*基本思想:就是两个数字对应相加,如果两个链表长度不一样,那短的那个算0来相加。最后都遍历完,考虑最后多出的进位新建一个链表添加在后面
*/

/**
 * 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 *res = new ListNode(0);
        ListNode *r = res;
        ListNode *numlist1 = l1;
        ListNode *numlist2 = l2;
        int num1 ;
        int num2 ;
        int rest = 0;
        while(numlist1!=NULL || numlist2!=NULL)
        {
            ListNode *node = new ListNode(0);
            if(numlist1==NULL)
                num1 = 0;
            else
                num1 = numlist1->val;
            if(numlist2==NULL)
                num2 = 0;
            else
                num2 = numlist2->val;
        
            int num = (num1+num2+rest)%10 ;
            rest = (num1+num2+rest)/10;
            node->val = num;
            res->next = node;
            res = node;
            if(numlist1==NULL)
                numlist1=NULL;
            else
                numlist1 = numlist1->next;
            if(numlist2==NULL)
                numlist2=NULL;
            else                
                numlist2 = numlist2->next;
        }
        if(rest!=0)
        {
            ListNode *node = new ListNode(rest);
            res->next = node;
        }
        return r->next;
    }
};

143. 重排链表

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。


链接:https://leetcode-cn.com/problems/reorder-list

/*基本思想:分三步,先利用快慢指针找到中点,然后分成左部分和右部分,对右部分反转,然后两个部分合并
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    void reorderList(ListNode* head) {
       
        if(head == NULL)
            return;
        
        //快慢指针找中点
        ListNode *quick = head;
        ListNode *low = head->next;
        
        while(low!=NULL && low->next!=NULL)
        {
            quick = quick->next;
            low = low->next->next;
        }  
        
        //反转右部分
        ListNode *Rhead = quick->next;
        quick->next = NULL;
        ListNode* pre = NULL;
        ListNode *cur = Rhead;
        while (cur != NULL) {
            ListNode* nextTemp = cur->next;
            cur->next = pre;
            pre = cur;
            cur = nextTemp;
        }

        Rhead = pre;

        //合并链表
        ListNode *res = new ListNode(0);
        ListNode *returnh = res;
        int i=1;
       
        while(head!=NULL && Rhead!=NULL)
        { 
         
        
            ListNode*l = head->next;       //记住下一个要找的位置
            ListNode*r = Rhead->next;
           
            if(i%2==0){
                res->next = Rhead;
               Rhead = r;
            }
            else
            {
                res->next = head;
                 head = l;
            }
            res = res->next;

            i++;
        
        }
       
        if(head == NULL)
            res->next = Rhead;
        if(Rhead == NULL)
            res->next = head;
        
        head = returnh->next;
    }
};

19. 删除链表的倒数第N个节点

给定一个链表,删除链表的倒数第 个节点,并且返回链表的头结点。

链接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/

/*基本思想:主要是要找到要删除的节点,三个指针,一个指要删除的节点的之前的节点,快指针先走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) {
        
        
        ListNode* newhead = new ListNode(0);
        newhead->next = head;
        ListNode* l1=newhead;
        ListNode* l2=newhead;
        ListNode* pre=newhead;
        
        while(n>0 && l2!=NULL)
        {
            l2=l2->next;
            n--;
        }
        
        while(l2!=NULL)
        {
            pre = l1;
            l1=l1->next;
            l2=l2->next;
          
        }
        
       
        pre->next = l1->next;
        
        return newhead->next;
    }
};

141. 环形链表

给定一个链表,判断链表中是否有环。

链接:https://leetcode-cn.com/problems/linked-list-cycle/

/*基本思想:利用快慢指针,如果两个指针相遇表示有环,否则没有环
*/

/**
 * 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) {
        
        ListNode* fast=head;
        ListNode* slow=head;
        
        while(fast!=NULL  && fast->next!=NULL)
        {
            fast = fast->next->next;
            slow=slow->next;
            if(fast==slow)
                return true;
        }
        return false;
    }
};

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

链接:https://leetcode-cn.com/problems/linked-list-cycle-ii/

基本思想:先利用快慢指针找到第一次相遇的位置,然后一个指针从头开始出发,一个指针从相遇位置开始,再次相遇的点就是环的入口
原理:


X,Y,Z分别为链表起始位置,环开始位置和两指针相遇位置。

由于快指针速度为慢指针速度的两倍,那么这个慢指针最多走圆的一圈(这里想象极端情况,就是整个链表就是一个环,那么两个指针从圆的同一个地方出发,那么此时何时相遇呢?必然是慢指针正好才走一圈的时候,快指针走两圈追上来了)。

所以这里假设就是在Z相遇的,那么慢指针走的距离是a+b,很好计算。而快指针走的距离是2(a+b),此时我们想象,假设慢指针走到了X和Z的中间的时候,快指针已经到Z了,那么下面再走的话,就是快指针从Z点出发围着圆绕几圈之后恰好在Z点和X相遇,因此快指针走过的距离是:

2(a+b) = a+b+n*圆的周长 = a+b+n(b+c)

此时a为:

a = (n - 1) * b + n * c = (n - 1)(b + c) +c

从公式上看,当一个指针从X出出发,走完a的距离之后,那么另一个指针从相遇点Z出发就会走(n-1)圈的环再加一个C的距离,此时正好在Y点相遇。

因此,一个指针从头出发,一个指针从相遇点出发,速度相同,相遇点就是环的入口节点。
*/
 

/*基本思想:先利用快慢指针找到第一次相遇的位置,然后一个指针从头开始出发,一个指针从相遇位置开始,再次相遇的点就是环的入口

*/

/**
 * 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) {
        
        ListNode *fast=head;
        ListNode *slow=head;
        
        if( fast== NULL || fast->next == NULL)
            return NULL;
        
        while(fast!=NULL && fast->next!=NULL)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow)
            {
                break;
            }
        }
         
        while(fast!=NULL && fast!=head )
        {
            fast = fast->next;
            head = head->next;
        }
        
       
        return fast;
    }
};

83. 删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list/

/*基本思想:对于每一个链表节点,while循环向后找第一个和他不一样的接到他后面
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* p=head;
        while(p!=NULL && p->next!=NULL)
        {
            
            while(p->next!=NULL && p->next->val == p->val)
            {  
                ListNode *next = p->next->next;
                p->next = next;
                
            }
            p =p->next;
            
        }
        return head; 
    }
};

82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

链接:https://leetcode-cn.com/problems/remove-duplicates-from-sorted-list-ii/

/*基本思想: 用一个新链表存储只出现一次的链表节点,和之前删除重复元素类似,先找到第一个和他不相等的,不过此时要有一个计数,当总数等于1的时候,才连接,否则不连接,注意最后一个当为空的时候要把新链表最后置为NULL(否则会连接以前存在的)
*/
/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode *newhead = new ListNode(0);
        ListNode *res = newhead;
        ListNode *p = head;
        while(p!=NULL)
        {
            int count = 1;
            while(p->next!=NULL && p->next->val == p->val )
            {
                count++;
                p->next = p->next->next;
            }
            if(count==1)
            {
                cout<<newhead->val<<endl;
                newhead->next = p;
                newhead=newhead->next;
            }
            if(p->next==NULL)
            {
                newhead->next =NULL;
            }
            p=p->next;
        }
    
        return res->next;
    }
};

116. 填充每个节点的下一个右侧节点指针

给定一个完美二叉树,其所有叶子节点都在同一层,每个父节点都有两个子节点。

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。

初始状态下,所有 next 指针都被设置为 NULL。


链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node

 

/*
    基本思路:主要是理解题,对于一个根节点,他的左孩子的next指向右孩子,对于右孩子的next指向根的next的左孩子
             所以只需要对于每一个根节点进行这样的操作就可以,递归处理每一个节点,体中初始的next已经是NULL
*/
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() {}

    Node(int _val, Node* _left, Node* _right, Node* _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/
class Solution {
public:
    Node* connect(Node* root) {
        
        if(root==NULL)
            return root;
        
        if(root->left!=NULL)
            root->left->next = root->right;
        
        if(root->right!=NULL && root->next)
            root->right->next = root->next->left;
        
        connect(root->left);
        connect(root->right);
        
        return root;
        
        
        
    }
};

117. 填充每个节点的下一个右侧节点指针 II

给定一个二叉树,不是完全二叉树
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。初始状态下,所有 next 指针都被设置为 NULL。


链接:https://leetcode-cn.com/problems/populating-next-right-pointers-in-each-node-ii

/*基本思想:和上一个思想一样,也是递归,这个要注意不是完全二叉树的时候,对于next指针可能要找到上一层的next的左右孩子,存在指向,都不存在才为NULL
           所以当树大的时候,一个节点的next可能指向的离他比较远,所以要遍历确定指向,同时注意要先确定root-right的next,root->left的next需要依赖于right的             next往后找
*/
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;
    Node* next;

    Node() {}

    Node(int _val, Node* _left, Node* _right, Node* _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/
class Solution {
public:
    Node* connect(Node* root) {
       if(root==NULL)
            return root;
        
        if(root->left!=NULL){
            if(root->right!=NULL)
                root->left->next = root->right;
            else 
            {
                Node* next=root->next;
                while(next) {
                if(next->left!=NULL){
                 
                    root->left->next = next->left;
                    break;
                }
                if(next->right!=NULL)
                {
                    root->left->next = next->right; 
                    break;
                }   
                    next =next->next;
                }
            }
        }
        if(root->right!=NULL )
        {
            Node* next =root->next;
            while(next) {
                if(next->left!=NULL) {
                   
                    root->right->next = next->left;
                    break;
                }
                
                 if(next->right!=NULL)
                {
                    root->right->next = next->right; 
                    break;
                }   
                    next= next->next;
                
            }
                
        }   
           
        
        connect(root->right);
        //先确保 root->right 下的节点的已完全连接,因 root->left 下的节点的连接
        // 需要 root->left.next 下的节点的信息,若 root.right 下的节点未完全连
        // 接(即先对 root.left 递归),则 root->left.next 下的信息链不完整,将
        // 返回错误的信息。可能出现的错误情况如下图所示。此时,底层最左边节点将无
        // 法获得正确的 next 信息:
        //                  o root
        //                 / \
        //     root.left  o —— o  root.right
        //               /    / \
        //              o —— o   o
        //             /        / \
        //            o        o   o 
        
        connect(root->left);
        return root;

    }
};


 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值