链表题目汇总
你有两个用链表代表的整数,其中每个节点包含一个数字。数字存储按照在原来整数中相反
的顺序,使得第一个数字位于链表的开头。写出一个函数将两个整数相加,用链表形式返回和。
样例
样例 1:
输入: 7->1->6->null, 5->9->2->null
输出: 2->1->9->null
样例解释: 617 + 295 = 912, 912 转换成链表: 2->1->9->null
样例 2:
输入: 3->1->5->null, 5->9->2->null
输出: 8->0->8->null
样例解释: 513 + 295 = 808, 808 转换成链表: 8->0->8->null
对题目理解不好,数据类型换成long 解答:通过70%
class Solution { public: /** * @param l1: the first list * @param l2: the second list * @return: the sum list of l1 and l2 */ ListNode * addLists(ListNode * l1, ListNode * l2) { // write your code here stack<int> res1,res2; if(l1==NULL ){ return l2; } if(l2==NULL){ return l1; } while(l1!=NULL){ res1.push(l1->val); l1=l1->next; } while(l2!=NULL){ res2.push(l2->val); l2=l2->next; } int len1=res1.size(); int len2=res2.size(); int num1 = 0; while(len1--){ int temp1=res1.top()*pow(10,len1); res1.pop(); num1 = num1+temp1; } int num2 = 0; while(len2--){ int temp2=res2.top()*pow(10,len2); res2.pop(); num2 = num2+temp2; } int num = num1+num2; cout<<num<<endl; vector<int> result; while(num>=10){ int temp = num%10; result.push_back(temp); num=num/10; } result.push_back(num); int l = result.size(); cout<<l<<endl; ListNode* head = new ListNode(result[0]); ListNode * temp =head; for(int i=1;i<l;i++){ ListNode* node = new ListNode(result[i]); cout<<node->val<<endl; temp->next=node; temp = node; } return head; } };
优秀解法:
/** * Definition of singly-linked-list: * class ListNode { * public: * int val; * ListNode *next; * ListNode(int val) { * this->val = val; * this->next = NULL; * } * } */ class Solution { public: /** * @param l1: the first list * @param l2: the second list * @return: the sum list of l1 and l2 */ ListNode *addLists(ListNode *l1, ListNode *l2) { // write your code here ListNode *head = new ListNode(0); ListNode *end = head; ListNode *curr1 = l1; ListNode *curr2 = l2; int carry = 0; // 深入理解题目,求和进位进到后面去了,所以从前往后遍历就行 while (curr1 != nullptr || curr2 != nullptr || carry != 0) { int a1 = 0; int a2 = 0; if (curr1 != nullptr) { a1 = curr1->val; curr1 = curr1->next; } if (curr2 != nullptr) { a2 = curr2->val; curr2 = curr2->next; } int sum = a1 + a2 + carry; carry = sum / 10; sum %= 10; ListNode *curr = new ListNode(sum); end->next = curr; end = curr; } return head->next; } };
165. 合并两个排序链表
将两个排序链表合并为一个新的排序链表
样例
样例 1:
输入: list1 = null, list2 = 0->3->3->null
输出: 0->3->3->null
样例2:
输入: list1 = 1->3->8->11->15->null, list2 = 2->null
输出: 1->2->3->8->11->15->null
/** * Definition of singly-linked-list: * class ListNode { * public: * int val; * ListNode *next; * ListNode(int val) { * this->val = val; * this->next = NULL; * } * } */ class Solution { public: /** * @param l1: ListNode l1 is the head of the linked list * @param l2: ListNode l2 is the head of the linked list * @return: ListNode head of linked list */ ListNode * mergeTwoLists(ListNode * l1, ListNode * l2) { // write your code here if(l1 ==NULL){ return l2; } if(l2== NULL){ return l1; } ListNode * head= NULL; if(l1->val >= l2->val){ head=l2; head->next = mergeTwoLists(l1,l2->next); } else{ head=l1; head->next = mergeTwoLists(l1->next,l2); } return head; } };
翻转链表:
/** * Definition of singly-linked-list: * * class ListNode { * public: * int val; * ListNode *next; * ListNode(int val) { * this->val = val; * this->next = NULL; * } * } */ class Solution { public: /** * @param head: n * @return: The new head of reversed linked list. */ ListNode * reverse(ListNode * head) { // write your code here if(head == NULL || head->next == NULL){ return head; } ListNode * head_new = NULL; ListNode * cur = head; while(cur!=NULL){ ListNode * temp = cur; cur = cur->next; ListNode * head_pre= head_new; head_new =temp; head_new->next = head_pre; head_new->val=temp->val; } return head_new; } };
// 找到单链表倒数第n个节点,保证链表中节点的最少数量为n。
// 样例
// Example 1:
// Input: list = 3->2->1->5->null, n = 2
// Output: 1
// 当语言有问题时,可以重置一下
class Solution { public: /* * @param head: The first node of linked list. * @param n: An integer * @return: Nth to last node of a singly linked list. */ ListNode * nthToLast(ListNode * head, int n) { // write your code here // 双指针问题解决 if(head==NULL || n<=0 ){ return NULL; } ListNode *fast=head; ListNode *slow=head; while(n--){ fast = fast->next; } while(fast!=NULL){ fast = fast->next; slow = slow->next; } return slow; } };
102. 带环链表
给定一个链表,判断它是否有环。
样例
```
样例 1:
输入: 21->10->4->5, then tail connects to node index 1(value 10).
输出: true
样例 2:
输入: 21->10->4->5->null
输出: false
```
挑战
不要使用额外的空间
/** * Definition of singly-linked-list: * class ListNode { * public: * int val; * ListNode *next; * ListNode(int val) { * this->val = val; * this->next = NULL; * } * } */ class Solution { public: /** * @param head: The first node of linked list. * @return: True if it has a cycle, or false */ bool hasCycle(ListNode * head) { // write your code here if(head==NULL || head->next==NULL){ return false; } ListNode* slow = head; ListNode* fast = head; // 注意,这里要避免野指针出现,判断快指针是否到头即可 while(fast !=NULL && fast->next !=NULL){ slow = slow->next; fast = fast->next->next; if(slow == fast){ return true; } } return false; } };
给出一个所有元素以升序排序的单链表,将它转换成一棵高度平衡的二叉搜索树
样例
样例 1:
输入: array = 1->2->3
输出:
2
/ \
1 3
样例 2:
输入: 2->3->6->7
输出:
3
/ \
2 6
\
7
解释:
可能会有多个符合要求的结果,返回任意一个即可。
/** * Definition of ListNode * class ListNode { * public: * int val; * ListNode *next; * ListNode(int val) { * this->val = val; * this->next = NULL; * } * } * Definition of TreeNode: * class TreeNode { * public: * int val; * TreeNode *left, *right; * TreeNode(int val) { * this->val = val; * this->left = this->right = NULL; * } * } */ // class Solution { // public: // /* // * @param head: The first node of linked list. // * @return: a tree node // */ // TreeNode * sortedListToBST(ListNode * head) { // // write your code here // if(head==NULL){ // return NULL; // } // if(head->next==NULL){ // TreeNode* t_node=new TreeNode(head->val); // return t_node; // } // if(head->next->next==NULL){ // TreeNode* t_node=new TreeNode(head->val); // t_node->right = new TreeNode(head->next->val); // return t_node; // } // int len = 0; // ListNode * temp = head; // while(temp){ // len++; // temp = temp->next; // } // int mid = len/2; // ListNode * mid_node = head; // while(mid--){ // mid_node = mid_node->next; // } // ListNode * mid_next = mid_node->next; // mid_node->next == NULL; // TreeNode* root = new TreeNode(mid_node->val); // root->left = sortedListToBST(head); // root->right = sortedListToBST(mid_next); // return root; // } // }; class Solution { public: TreeNode* sortedListToBST(ListNode* head) { vector<int> v; ListNode* cur=head; while(cur!=NULL){ v.push_back(cur->val); cur=cur->next; } TreeNode* root=buildBST(v,0,v.size()-1); return root; } TreeNode* buildBST(vector<int>& nums,int l,int r){ if(l>r) return NULL; int mid=(l+r)/2; auto node= new TreeNode(nums[mid]); node->left=buildBST(nums,l,mid-1); node->right=buildBST(nums,mid+1,r); return node; } };
剑指 Offer 52. 两个链表的第一个公共节点
输入两个链表,找出它们的第一个公共节点。
如下面的两个链表:
在节点 c1 开始相交。
示例 1:
输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。
/** * 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==NULL || headB==NULL){ return NULL; } ListNode *A = headA; int lenA=0; ListNode *B = headB; int lenB=0; while(A!=NULL){ lenA++; A=A->next; } while(B!=NULL){ lenB++; B=B->next; } int step= 0; ListNode *tempA=headA; ListNode *tempB=headB; if(lenA>lenB){ step= lenA-lenB; while(step){ tempA=tempA->next; step--; } while(tempA!=NULL&&tempB!=NULL){ if(tempA==tempB){ return tempA; } tempA=tempA->next; tempB=tempB->next; } }else{ step= lenB-lenA; while(step){ tempB=tempB->next; step--; } while(tempA!=NULL&&tempB!=NULL){ if(tempA==tempB){ return tempA; } tempA=tempA->next; tempB=tempB->next; } } return NULL; } };
思路:快慢指针
遍历两个链表,然后计算两个链表的节点数量,计算两个链表的节点数量差;
然后重新开始遍历,节点多的链表先开始,走多出来的节点个步数,然后节点少的链表和节点多的链表同时开始遍历,当指向的位置相同时,即为第一个交点;
给你一个链表数组,每个链表都已经按升序排列。
请你将所有链表合并到一个升序链表中,返回合并后的链表。
示例 1:
输入:lists = [[1,4,5],[1,3,4],[2,6]] 输出:[1,1,2,3,4,4,5,6] 解释:链表数组如下: [ 1->4->5, 1->3->4, 2->6 ] 将它们合并到一个有序链表中得到。 1->1->2->3->4->4->5->6
示例 2:
输入:lists = [] 输出:[]
示例 3:
输入:lists = [[]] 输出:[]
提示:
k == lists.length
0 <= k <= 10^4
0 <= lists[i].length <= 500
-10^4 <= lists[i][j] <= 10^4
lists[i]
按 升序 排列lists[i].length
的总和不超过10^4
两种思路:
1)使用优先队列的方法,每个链表取一个元素放入优先队列,每次从优先队列里取最小的那个元素
2)归并排序的思想,有序链表两两合并
https://blog.csdn.net/aikudexue/article/details/90769813
class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { priority_queue<ListNode*, vector<ListNode*>, compare> q; for (auto l : lists) { if (l) { q.push(l); //每个链表取一个元素放入优先队列 } } ListNode pre(0); //新建一个头结点,可以防止丢失最终链表的头 ListNode *node = &pre while (q.size()) { ListNode *top = q.top(); //从优先队列里取最小的那个元素 q.pop(); node->next = top; //将弹出的那个元素连到最终链表上 node = node->next; //指针向后移一步 if (top->next) { q.push(top->next); //将下一个元素入队列 } } return pre.next; } struct compare { bool operator()(const ListNode* l1, const ListNode* l2) { return l1->val > l2->val; } }; };
/** * 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* mergeKLists(vector<ListNode*>& lists) { // priority_queue<ListNode*, vector<ListNode*>, compare> q; // for (auto l : lists) { // if (l) { // q.push(l); //每个链表取一个元素放入优先队列 // } // } // ListNode pre(0); //新建一个头结点,可以防止丢失最终链表的头 // ListNode *node = ⪯ // while (q.size()) { // ListNode *top = q.top(); //从优先队列里取最小的那个元素 // q.pop(); // node->next = top; //将弹出的那个元素连到最终链表上 // node = node->next; //指针向后移一步 // if (top->next) { // q.push(top->next); //将下一个元素入队列 // } // } // return pre.next; // } // struct compare { // bool operator()(const ListNode* l1, const ListNode* l2) { // return l1->val > l2->val; // } // }; // }; class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { return partition(lists, 0, lists.size()-1); //合并0-(size-1)的链表 } ListNode* partition(vector<ListNode*>& lists, int start, int end){ if(start == end){ return lists[start]; } if(start < end){ int mid = (end+start)/2; ListNode* l1 = partition(lists, start, mid); //合并start->mid的链表 ListNode* l2 = partition(lists, mid+1, end); //合并mid+1到end的链表 //partition最终递归到最后还是两个链表的合并.举个例子来说比如合并6个链表,那么按照分治法,我们首先分别合并1和4,2和5,3和6。这样下一次只需合并3个链表,我们再合并1和3,最后和2合并就可以了。 return merge(l1, l2); } return NULL; } ListNode* merge(ListNode* l1, ListNode* l2){ //合并L1,L2两个有序链表 if(!l1) return l2; if(!l2) return l1; if(l1->val < l2->val){ l1->next = merge(l1->next, l2); return l1; }else{ l2->next = merge(l1, l2->next); return l2; } } };
重新写的思路:
1)先合并两个
2)然后两两合并
class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode *newHead=new ListNode(0); ListNode *head=newHead; while(l1!=NULL&&l2!=NULL){ if(l1->val>l2->val){ newHead->next=l2; l2=l2->next; newHead=newHead->next; }else{ newHead->next=l1; l1=l1->next; newHead=newHead->next; } } if(l1!=NULL&&l2==NULL){ newHead->next=l1; }else if(l1==NULL&&l2!=NULL){ newHead->next=l2; } return head->next; } ListNode* mergeKLists(vector<ListNode*>& lists) { if(lists.size()==0){ return NULL; } int interval =1; int len = lists.size(); while(interval<len){ for(int i=0;i<len-interval;){ lists[i]=mergeTwoLists(lists[i],lists[i+1]); i=i+interval*2; } interval = interval*2; } return lists[0]; } };
19. 删除链表的倒数第N个节点
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.
说明:
给定的 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) { // 双指针问题解决 if(head==NULL || n<=0 ){ return NULL; } if(head->next==NULL){ return NULL; } ListNode *fast=head; ListNode *slow=head; while(n--){ fast = fast->next; } ListNode * temp = NULL; while(fast!=NULL){ fast = fast->next; temp = slow; slow = slow->next; } if(slow==head){ ListNode * head_new = slow->next; slow->next = NULL; return head_new; } else{ temp->next= slow->next; slow->next=NULL; } return head; } };
83. 删除排序链表中的重复元素
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3
思路:很简单,遍历链表,因为链表有序,依次比较当前元素和下一个元素是否相等,相等则删除下一个元素,将链表指针重新指向;
递归这个过程,直到链表结束,一次AC
/** * 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) { if(head==NULL||head->next==NULL){ return head; } ListNode* cur = head; while(cur->next!=NULL){ if(cur->val==cur->next->val){ cur->next=cur->next->next; deleteDuplicates(head); } else{ cur=cur->next; } } return head; } };
61. 旋转链表
给定一个链表,旋转链表,将链表每个节点向右移动 k 个位置,其中 k 是非负数。
示例 1:
输入: 1->2->3->4->5->NULL, k = 2
输出: 4->5->1->2->3->NULL
解释:
向右旋转 1 步: 5->1->2->3->4->NULL
向右旋转 2 步: 4->5->1->2->3->NULL
示例 2:
输入: 0->1->2->NULL, k = 4
输出: 2->0->1->NULL
解释:
向右旋转 1 步: 2->0->1->NULL
向右旋转 2 步: 1->2->0->NULL
向右旋转 3 步: 0->1->2->NULL
向右旋转 4 步: 2->0->1->NULL
解题思路:
step1:将链表首位相连成环;
step2:在k = list_len - k % list_len处断开。
如果理解起来比较抽象,可以画个图帮助理解,找到断开点以及返回新的头节点;
注意链表遍历cur和cur2不要用错,--k 还是 k--要验证清楚
/** * 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){ return head; } ListNode* cur = head; ListNode* cur2 = head; int len = 1; while(cur->next!=NULL){ cur= cur->next; len++; } cur->next = head; k = len-k%len; while(--k){ cur2=cur2->next; } ListNode* head_new = cur2->next; cur2->next=NULL; return head_new; } };
思路参考:https://blog.csdn.net/starzyh/article/details/90345257
138. 复制带随机指针的链表
给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。
要求返回这个链表的 深拷贝。
我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:
val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。
思路:
第一次遍历链表的时候,复制旧链表的节点值建立一个新的链表,同时定义一个 unordered_map 作为哈希表,哈希表的键为旧链表的节点指针,值为新链表的节点指针。
然后,第二次遍历链表,访问旧链表节点的随机指针,然后以此为键从 map 中取出对应的新链表节点指针,这也就是当前新链表节点的随机指针。
https://www.cnblogs.com/seniusen/p/10142869.html
/* // Definition for a Node. class Node { public: int val; Node* next; Node* random; Node(int _val) { val = _val; next = NULL; random = NULL; } }; */ class Solution { public: Node *copyRandomList(Node *head) { unordered_map<Node *, Node *> nodemap; Node *temp = head; Node *new_head = new Node(0); //哨兵节点,方便操作 Node *copy_temp = new_head; // 建立新链表 while (temp) { copy_temp->next = new Node(temp->val); nodemap[temp] = copy_temp->next; temp = temp->next; copy_temp = copy_temp->next; } Node *random_temp = NULL; temp = head; copy_temp = new_head->next; // 填充新链表的随机指针 while (temp) { random_temp = temp->random; if (random_temp != NULL) copy_temp->random = nodemap[random_temp]; else copy_temp->random = NULL; temp = temp->next; copy_temp = copy_temp->next; } return new_head->next; } };
876. 链表的中间结点
给定一个头结点为 head 的非空单链表,返回链表的中间结点。
如果有两个中间结点,则返回第二个中间结点。
示例 1:
输入:[1,2,3,4,5]
输出:此列表中的结点 3 (序列化形式:[3,4,5])
返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。
注意,我们返回了一个 ListNode 类型的对象 ans,这样:
ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.
示例 2:
输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。
思路:快慢指针,注意奇数个节点和偶数个节点,设置一个标志未来处理
1--2--3--4--5
1--2--3--4
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* middleNode(ListNode* head) { if(head ->next == NULL){ return head; } if(head->next->next==NULL){ return head->next; } ListNode* slow = head; ListNode* fast =head; int flag =1; while(fast->next!=NULL&&fast->next->next!=NULL){ fast = fast->next->next; slow = slow->next; } if(fast->next!=NULL){ flag=0; } if(flag==0){ return slow->next; } return slow; } };