在开始说链表之前首先推荐大家看一篇文章来巩固一下链表的知识
这里
原题
Reverse a singly linked list.Example:Input: 1->2->3->4->5->NULL
Output: 5->4->3->2->1->NULL
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
// Iterative
// Time Complexity: O(n)
// Space Complexity: O(1)
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = NULL;
ListNode* cur = head;
while(cur != NULL){
ListNode* next = cur->next;
cur->next = pre;
pre = cur;
cur = next;
}
return pre;
}
};
ListNode* createLinkedList(int arr[], int n){
if(n == 0){
return NULL;
}
ListNode* head = new ListNode(arr[0]);
ListNode* curNode = head;
for (int i = 1; i < n; i++) {
curNode->next = new ListNode(arr[i]);
curNode = curNode->next;
}
return head;
}
void printLinkedList(ListNode* head){
ListNode* curNode = head;
while (curNode != NULL){
cout<< curNode->val << " -> ";
curNode = curNode->next;
}
cout<<"NULL"<<endl;
return;
}
void deleteLinkedList(ListNode* head){
ListNode* curNode = head;
while (curNode != NULL){
ListNode* delNode = curNode;
curNode = curNode->next;
delete delNode;
}
}
原题
Reverse a linked list from position m to n. Do it in one-pass.Note: 1 ≤ m ≤ n ≤ length of list.Example:Input: 1->2->3->4->5->NULL, m = 2, n = 4
Output: 1->4->3->2->5->NULL
对于链表的问题,根据以往的经验一般都是要建一个dummy node,连上原链表的头结点,这样的话就算头结点变动了,我们还可以通过dummy->next来获得新链表的头结点。这道题的要求是只通过一次遍历完成,就拿题目中的例子来说,变换的是2,3,4这三个点,我们需要找到第一个开始变换结点的前一个结点,只要让pre向后走m-1步即可,为啥要减1呢,因为题目中是从1开始计数的,这里只走了1步,就是结点1,用pre指向它。万一是结点1开始变换的怎么办,这就是我们为啥要用dummy结点了,pre也可以指向dummy结点。然后就要开始交换了,由于一次只能交换两个结点,所以我们按如下的交换顺序:
1 -> 2 -> 3 -> 4 -> 5 -> NULL
1 ->3 -> 2 -> 4 -> 5 -> NULL
1 -> 4 -> 3 -> 2 -> 5 -> NULL
我们可以看出来,总共需要n-m步即可,第一步是将结点3放到结点1的后面,第二步将结点4放到结点1的后面。这是很有规律的操作,那么我们就说一个就行了,比如刚开始,pre指向结点1,cur指向结点2,然后我们建立一个临时的结点t,指向结点3(注意我们用临时变量保存某个结点就是为了首先断开该结点和前面结点之间的联系,这可以当作一个规律记下来),然后我们断开结点2和结点3,将结点2的next连到结点4上,也就是 cur->next = t->next,再把结点3连到结点1的后面结点(即结点2)的前面,即 t->next = pre->next,最后再将原来的结点1和结点2的连接断开,将结点1连到结点3,即 pre->next = t。这样我们就完成了将结点3取出,加入结点1的后方。第二步将结点4取出,加入结点1的后方,也是同样的操作,这里就不多说了,请大家自己尝试下吧,参见代码如下:
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int m, int n) {
ListNode *dummy = new ListNode(-1), *pre = dummy;
dummy->next = head;
for (int i = 0; i < m - 1; ++i) pre = pre->next;
ListNode *cur = pre->next;
for (int i = m; i < n; ++i) {
ListNode *t = cur->next;
cur->next = t->next;
t->next = pre->next;
pre->next = t;
}
return dummy->next;
}
};
原题
Given a sorted linked list, delete all duplicates such that each element appear only once.Example 1:Input: 1->1->2
Output: 1->2
Example 2:Input: 1->1->2->3->3
Output: 1->2->3
这道题让我们移除给定有序链表的重复项,那么可以遍历这个链表,每个结点和其后面的结点比较,如果结点值相同了,只要将前面结点的 next 指针跳过紧挨着的相同值的结点,指向后面一个结点。这样遍历下来,所有重复的结点都会被跳过,留下的链表就是没有重复项的了,代码如下:
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
ListNode *cur = head;
while (cur && cur->next) {
if (cur->val == cur->next->val) {
cur->next = cur->next->next;
} else {
cur = cur->next;
}
}
return head;
}
};
我们也可以使用递归的方法来做,首先判断是否至少有两个结点,若不是的话,直接返回 head。否则对 head->next 调用递归函数,并赋值给 head->next。这里可能比较晕,先看后面一句,返回的时候,head 结点先跟其身后的结点进行比较,如果值相同,那么返回后面的一个结点,当前的 head 结点就被跳过了,而如果不同的话,还是返回 head 结点。可以发现了,进行实质上的删除操作是在最后一句进行了,再来看第二句,对 head 后面的结点调用递归函数,那么就应该 suppose 返回来的链表就已经没有重复项了,此时接到 head 结点后面,在第三句的时候再来检查一下 head 是否又 duplicate 了,实际上递归一直走到了末尾结点,再不断的回溯回来,进行删除重复结点,参见代码如下:
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if (!head || !head->next) return head;
head->next = deleteDuplicates(head->next);
return (head->val == head->next->val) ? head->next : head;
}
};
原题
Given a linked list and a value x, partition it such that all nodes less than x come before nodes greater than or equal to x.You should preserve the original relative order of the nodes in each of the two partitions.Example:Input: head = 1->4->3->2->5->2, x = 3
Output: 1->2->2->4->3->5
/// Linear Scan
/// Time Complexity: O(n)
/// Space Complexity: O(1)
class Solution {
public:
static ListNode* partition(ListNode* head, int x) {
ListNode* dummyHead1 = new ListNode(-1);
ListNode* dummyHead2 = new ListNode(-1);
ListNode* prev1 = dummyHead1;
ListNode* prev2 = dummyHead2;
for(ListNode* cur = head ; cur != NULL ;){
if(cur->val < x){
prev1->next = cur;
cur = cur->next;
prev1 = prev1->next;
prev1->next = NULL;
}
else{
prev2->next = cur;
cur = cur->next;
prev2 = prev2->next;
prev2->next = NULL;
}
}
prev1->next = dummyHead2->next;
ListNode* ret = dummyHead1->next;
delete dummyHead1;
delete dummyHead2;
return ret;
}
};
原题
Given a singly linked list, group all odd nodes together followed by the even nodes. Please note here we are talking about the node number and not the value in the nodes.You should try to do it in place. The program should run in O(1) space complexity and O(nodes) time complexity.Example 1:Input: 1->2->3->4->5->NULL
Output: 1->3->5->2->4->NULL
Example 2:Input: 2->1->3->5->6->4->7->NULL
Output: 2->3->6->7->1->5->4->NULL
class Solution {
public:
ListNode* oddEvenList(ListNode* head) {
ListNode* dummyHead1 = new ListNode(-1);
ListNode* dummyHead2 = new ListNode(-1);
ListNode* prev1 = dummyHead1;
ListNode* prev2 = dummyHead2;
ListNode* cur = head;
for(int i = 0 ; cur ; i++){
if(i % 2 == 0){
prev1->next = cur;
cur = cur->next;
prev1 = prev1->next;
prev1->next = NULL;
}
else{
prev2->next = cur;
cur = cur->next;
prev2 = prev2->next;
prev2->next = NULL;
}
}
prev1->next = dummyHead2->next;
ListNode* ret = dummyHead1->next;
delete dummyHead1;
delete dummyHead2;
return ret;
}
};
原题
**You are given two non-empty linked lists representing two non-negative integers. The digits are stored in reverse order and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.You may assume the two numbers do not contain any leading zero, except the number 0 itself.Example:Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation: 342 + 465 = 807
这道并不是什么难题,算法很简单,链表的数据类型也不难,就是建立一个新链表,然后把输入的两个链表从头往后撸,每两个相加,添加一个新节点到新链表后面。为了避免两个输入链表同时为空,我们建立一个 dummy 结点,将两个结点相加生成的新结点按顺序加到 dummy 结点之后,由于 dummy 结点本身不能变,所以用一个指针 cur 来指向新链表的最后一个结点。好,可以开始让两个链表相加了,这道题好就好在最低位在链表的开头,所以可以在遍历链表的同时按从低到高的顺序直接相加。while 循环的条件两个链表中只要有一个不为空行,由于链表可能为空,所以在取当前结点值的时候,先判断一下,若为空则取0,否则取结点值。然后把两个结点值相加,同时还要加上进位 carry。然后更新 carry,直接 sum/10 即可,然后以 sum%10 为值建立一个新结点,连到 cur 后面,然后 cur 移动到下一个结点。之后再更新两个结点,若存在,则指向下一个位置。while 循环退出之后,最高位的进位问题要最后特殊处理一下,若 carry 为1,则再建一个值为1的结点,代码如下:
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode *dummy = new ListNode(-1), *cur = dummy;
int carry = 0;
while (l1 || l2) {
int val1 = l1 ? l1->val : 0;
int val2 = l2 ? l2->val : 0;
int sum = val1 + val2 + carry;
carry = sum / 10;
cur->next = new ListNode(sum % 10);
cur = cur->next;
if (l1) l1 = l1->next;
if (l2) l2 = l2->next;
}
if (carry) cur->next = new ListNode(1);
return dummy->next;
}
};
附上文章的链接:链接
原题
You are given two non-empty linked lists representing two non-negative integers. The most significant digit comes first and each of their nodes contain a single digit. Add the two numbers and return it as a linked list.You may assume the two numbers do not contain any leading zero, except the number 0 itself.Follow up:
What if you cannot modify the input lists? In other words, reversing the lists is not allowed.Example:Input: (7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 8 -> 0 -> 7
就是在上一题的基础上加一个链表的翻转
/// Using reverse
/// Time Complexity: O(m + n + max(m, n))
/// Space Complexity: O(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) {
l1 = reverse(l1);
l2 = reverse(l2);
ListNode* dummyHead = new ListNode(-1), *cur = dummyHead;
int carry = 0;
for(ListNode* node1 = l1, *node2 = l2; node1 || node2 || carry;
node1 = node1 ? node1->next : NULL, node2 = node2 ? node2->next : NULL){
int x = node1 ? node1->val : 0;
x += node2 ? node2->val : 0;
x += carry;
cur->next = new ListNode(x % 10);
cur = cur->next;
carry = x / 10;
}
return reverse(dummyHead->next);
}
private:
ListNode* reverse(ListNode* node){
if(!node->next) return node;
ListNode* ret = reverse(node->next);
node->next->next = node;
node->next = NULL;
return ret;
}
};
原题
Remove all elements from a linked list of integers that have value val.Example:Input: 1->2->6->3->4->5->6, val = 6
Output: 1->2->3->4->5
解法一:相对比较繁琐,要考虑头结点是否等于val的情况
/// Recursive
/// Time Complexity: O(n)
/// Space Complexity: O(n)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
while (head != NULL && head->val == val){
ListNode* delNode = head;
head = delNode->next;
delete delNode;
}
if (head == NULL){
return NULL;
}
ListNode* cur = head;
while (cur->next != NULL){
if (cur->next->val == val){
ListNode* delNode = cur->next;
cur->next = delNode->next;
delete delNode;
} else{
cur = cur->next;
}
}
return head;
}
};
解法二:解决了头结点的情况,利用了虚拟指针
/// Linear Scan with dummy head
/// Time Complexity: O(n)
/// Space Complexity: O(1)
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
ListNode* dummHead = new ListNode(0);
dummHead->next = head;
ListNode* cur = dummHead;
while (cur->next != NULL){
if (cur->next->val == val){
ListNode* delNode = cur->next;
//删除cur->next
cur->next = delNode->next;
delete delNode;
} else{
cur = cur->next;
}
}
ListNode* ret = dummHead->next;
delete dummHead;
return ret;
}
};
原题
Given a sorted linked list, delete all nodes that have duplicate numbers, leaving only distinct numbers from the original list.Example 1:Input: 1->2->3->3->4->4->5
Output: 1->2->5
Example 2:Input: 1->1->1->2->3
Output: 2->3
class Solution {
public:
static ListNode* deleteDuplicates(ListNode* head) {
ListNode* dummyHead = new ListNode(-1);
dummyHead->next = head;
ListNode* prev = dummyHead;
ListNode* cur = prev->next;
while(cur){
int num = 0;
ListNode* p = cur;
//把重复的结点移除掉
while (p && p->val == cur->val){
num++;
p = p->next;
}
//让dummyhead保存结果
if(num > 1){
prev->next = p;
} else{
prev = cur;
}
cur = p;
}
return dummyHead->next;
}
};
Given a linked list, swap every two adjacent nodes and return its head.You may not modify the values in the list’s nodes, only nodes itself may be changed. Example:Given 1->2->3->4, you should return the list as 2->1->4->3.
// Time Complexity: O(n)
// Space Complexity: O(1)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* p = dummyHead;
while(p->next && p->next->next){
ListNode* node1 = p->next;
ListNode* node2 = node1->next;
ListNode* next = node2->next;
node2->next = node1;
node1->next = next;
p->next = node2;
p = node1; //为了继续下一步循环,这时node1是靠后的结点,就是要交换结点的前一个结点
}
ListNode* retHead = dummyHead->next;
delete dummyHead;
return retHead;
}
};
Given a linked list, reverse the nodes of a linked list k at a time and return its modified list.k is a positive integer and is less than or equal to the length of the linked list. If the number of nodes is not a multiple of k then left-out nodes in the end should remain as it is.Example:Given this linked list: 1->2->3->4->5For k = 2, you should return: 2->1->4->3->5For k = 3, you should return: 3->2->1->4->5
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* dummyHead = new ListNode(-1);
dummyHead->next = head;
ListNode* pre = dummyHead;
while(pre && pre->next){
ListNode* end = pre;
int i;
for(i = 0; i < k && end->next; i ++)
end = end->next;
if(i != k) break; //这是判断第一次反转后,剩余部分是否是k的整数倍,不是就跳出循坏
ListNode* next = end->next; //保存未反转的结点
// reverse from pre->next to end
ListNode* rhead = reverse(pre->next, end);
ListNode* tail = pre->next; //为了更新pre后面未进行反转的部分而申请一个哨兵tail
pre->next = rhead; //更新pre和dummyHead前面已进行反转的结点
tail->next = next; //用tail保存还没进行反转的结点,同时也更新了pre和dummyHead后面未反转的结点
pre = tail; //因为pre是判断循环的条件,所以要把pre指向tail
}
ListNode* ret = dummyHead->next;
delete dummyHead;
return ret;
}
private:
ListNode* reverse(ListNode* head, ListNode* end){
if(head == end) return head;
ListNode* rhead = reverse(head->next, end);
head->next->next = head; //更新
return rhead;
}
};
这里顺带附上另一种解法,来自大佬的解法:
将单链表的每K个节点之间逆序
原题
class Solution {
public:
ListNode* insertionSortList(ListNode* head) {
if(!head || !head->next) return head;
ListNode* dummyHead = new ListNode(-1);
dummyHead->next = head;
ListNode* pre = dummyHead->next;
while(pre->next){
int val = pre->next->val;
ListNode* next = pre->next->next;
ListNode* pi = dummyHead;
for(; pi != pre; pi = pi->next)
if(pi->next->val > val){
ListNode* pj = pi->next; //当前结点,即值较大的结点
ListNode* swapNode = pre->next; //保存值较小的结点
//进行交换操作
pi->next = swapNode;
swapNode->next = pj;
pre->next = next;
break;
}
if(pi == pre) pre = pre->next;
// printLinkedList(dummyHead);
}
return dummyHead->next;
}
};
这题删除结点跟之前的删除结点不一样,因为题目只给出一个参数给我们,我们可以通过该结点的next结点的值去覆盖要删除的值(这个操作相当于已经把要删除的结点删除了),接着会出现两个重复的结点,然后再把后面的重复的结点删除即可
原题
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node.Given linked list – head = [4,5,1,9], which looks like following:
// 时间复杂度: O(1)
// 空间复杂度: O(1)
class Solution {
public:
void deleteNode(ListNode* node) {
assert(node != NULL && node->next != NULL);
node->val = node->next->val; //赋值,把要删除的结点的值覆盖
ListNode* delNode = node->next; //出现了重复,要删除的结点的后一个结点删除
node->next = delNode->next;
delete delNode;
return;
}
};
Given a linked list, remove the n-th node from the end of list and return its head.Example:Given linked list: 1->2->3->4->5, and n = 2.
After removing the second node from the end, the linked list becomes 1->2->3->5.
通过双指针,找出要删除结点的前一个结点
/// Two Pointers - One Pass Algorithm
/// Time Complexity: O(n)
/// Space Complexity: O(1)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* p = dummyHead;
ListNode* q = dummyHead;
//已经p和q的固定长度是n,先要找到q的位置
for(int i = 0 ; i < n + 1 ; i ++){
assert(q);
q = q->next;
}
//找要删除结点的前一个结点的位置
while(q){
p = p->next;
q = q->next;
}
ListNode* delNode = p->next;
p->next = delNode->next;
delete delNode;
ListNode* retNode = dummyHead->next;
delete dummyHead;
return retNode;
}
};
Given a linked list, rotate the list to the right by k places, where k is non-negative.Example 1:Input: 1->2->3->4->5->NULL, k = 2
Output: 4->5->1->2->3->NULL
Explanation:
rotate 1 steps to the right: 5->1->2->3->4->NULL
rotate 2 steps to the right: 4->5->1->2->3->NULL
Example 2:Input: 0->1->2->NULL, k = 4
Output: 2->0->1->NULL
Explanation:
rotate 1 steps to the right: 2->0->1->NULL
rotate 2 steps to the right: 1->2->0->NULL
rotate 3 steps to the right: 0->1->2->NULL
rotate 4 steps to the right: 2->0->1->NULL
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if(head == NULL)
return NULL;
int len = get_len(head);
k = k % len;//当k大于链表长度和k远远大于链表长度时该如何处理,我们需要首先遍历一遍原链表得到链表长度n,然后k对n取余,这样k肯定小于n
ListNode* end = head;//快指针
//快指针先走k步
for(int i = 0 ; i < k ; i ++)
end = end->next;
ListNode* start = head;//慢指针
while(end->next != NULL){
//然后两个指针一起走,当快指针走到末尾时,慢指针的下一个位置是新的顺序的头结点
start = start->next;
end = end->next;
}
//这时候start->next就是旋转两次后的开始结点
end->next = head; //第一次旋转的结果
head = start->next; //第二次旋转的结果
start->next = NULL; //置空
return head;
}
private:
int get_len(ListNode* head){
int res = 0;
while(head){
res ++;
head = head->next;
}
return res;
}
};
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…You may not modify the values in the list’s nodes, only nodes itself may be changed.Example 1:Given 1->2->3->4, reorder it to 1->4->2->3.Example 2:Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
class Solution {
public:
void reorderList(ListNode* head) {
if(!head || !head->next) return;
ListNode* slow = head, *fast = head;
while(fast->next && fast->next->next)
slow = slow->next, fast = fast->next, fast = fast->next;
ListNode* head1 = head, *head2 = slow->next;//断开两个链表
slow->next = NULL;//中点
head2 = reverse(head2);
//开始间隔插入操作
ListNode* dummyHead = new ListNode(-1);
ListNode* cur= dummyHead, *cur1 = head1, *cur2 = head2;
for(int i = 0; cur1 || cur2; i ++)
if(i % 2 == 0) cur->next = cur1, cur = cur->next, cur1 = cur1->next;
else cur->next = cur2, cur = cur->next, cur2 = cur2->next;
head = dummyHead->next;
}
private:
ListNode* reverse(ListNode* node){
if(!node->next) return node;
ListNode* ret = reverse(node->next);
node->next->next = node;
node->next = NULL;
return ret;
}
};
Given a singly linked list, determine if it is a palindrome.Example 1:Input: 1->2
Output: falseExample 2:Input: 1->2->2->1
Output: true
/// Two Pointers to Reverse and Traverse
/// Time Complexity: O(n)
/// Space Complexity: O(1)
class Solution {
public:
bool isPalindrome(ListNode* head) {
if(head == NULL || head->next == NULL)
return true;
ListNode* slow = head;//慢指针
ListNode* fast = head;//快指针
//slwo最终指向中间结点
while(fast->next != NULL && fast->next->next != NULL){
slow = slow->next;
fast = fast->next->next;
}
//把后半部分反转
slow->next = reverse(slow->next);
slow = slow->next;
ListNode* cur = head;
while(slow != NULL){
if(cur->val != slow->val)
return false;
else{
//继续往后比较
slow = slow->next;
cur = cur->next;
}
}
//比较完成
return true;
}
private:
ListNode* reverse(ListNode* head) {
if (head == NULL || head->next == NULL) {
return head;
}
ListNode *newList = reverse(head->next);
head->next->next = head;
head->next = NULL;
return newList;
}
};
写完这篇文章,正式结束1月份的学习了,1月份的下半月主要是学习javaweb和算法,之前也写过了不少的原创文章,虽然都是一些个人的小笔记,但是还是挺有用的,回想这半个多月,还是较好地完成了前期的任务,但是还是有需要改进的地方: