1. 对给定的链表进行排序,要求复杂度为O(nlgn)。
算法思想是分治,这样可以达到O(nlgn)。实现时写一个经典getMid函数,然后写合并函数,合并函数可以考虑使用一个多余辅助。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *sortList(ListNode *head) {
if (head == NULL || head->next==NULL) return head;
ListNode* mid = getMid(head);
return merge(sortList(head), sortList(mid));
}
private:
ListNode *getMid(ListNode* head){
ListNode* res = head;
if (head && head->next){
ListNode *fast = head->next, *slow = head;
while (fast && fast->next){
fast = fast->next->next;
slow = slow->next;
}
res = slow->next;
slow->next = NULL;
}
return res;
}
ListNode* merge(ListNode* p1, ListNode* p2){
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
while (p1 && p2){
if (p1->val < p2->val){
cur->next = p1;
p1 = p1->next;
}
else{
cur->next = p2;
p2 = p2->next;
}
cur = cur->next;
}
cur->next = p1 ? p1 : p2;
return dummy->next;
}
};
2. reorder-list。将链表按照如下要求的顺序输出。具体描述如下:
Given a singly linked list L: L0→L1→…→Ln-1→Ln,
reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
You must do this in-place without altering the nodes' values.
For example,
Given{1,2,3,4}, reorder it to{1,4,2,3}.
class Solution {
public:
void reorderList(ListNode *head) {
ListNode* mid = getMid(head);
reverse(mid);
merge(head, mid);
}
private:
void merge(ListNode*& p1, ListNode* p2){
if (p2 == NULL) return;
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
bool flag = true;
while (p1 && p2){
if (flag){
cur->next = p1;
p1 = p1->next;
}
else{
cur->next = p2;
p2 = p2->next;
}
cur = cur->next;
flag = !flag;
}
cur->next = p2;
p1 = dummy->next;
return;
}
ListNode* getMid(ListNode* head){
ListNode* res = head;
if (head == NULL || head->next == NULL) return NULL;
ListNode *slow = head, *fast = head->next;
while (fast->next && fast->next->next){//这样可以保证l2长1
fast = fast->next->next;
slow = slow->next;
}
res = slow->next;
slow->next = NULL;
return res;
}
void reverse(ListNode*& head){
if (head == NULL) return;
ListNode* cur = NULL;
while (head && head->next){
ListNode* tmp = head->next;
head->next = cur;
cur = head;
head = tmp;
}
head->next = cur;
return;
}
};
使用快慢指针可以判断链表是否有环。
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head == NULL) return false;
ListNode *fast = head, *slow = head;
while (fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
return true;
}
return false;
}
};
4. 判断链表是否有环,如果有,找出环的起始点。
在快慢指针的基础上,如果相遇则有环,相遇时将fast指针从头部走起,当fast与slow指针再次相遇时,环的起始点就找到了。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if (head == NULL) return NULL;
ListNode *fast = head, *slow = head;
while (fast && fast->next){
fast = fast->next->next;
slow = slow->next;
if (fast == slow){
fast = head;
while (fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
}
return NULL;
}
};
5. 链表(带随机指针)的拷贝。
两种思路:第一种借助数据结构hash_map。先把节点创建出来,然后再根据hash_map设置其random指针。
第二种思路是:每创建一个节点,将该节点插入到源节点的后边,然后再设置随机指针,最后再断开成为新的链表,实现拷贝。
以下是第二种思路的代码:
/**
* Definition for singly-linked list with a random pointer.
* struct RandomListNode {
* int label;
* RandomListNode *next, *random;
* RandomListNode(int x) : label(x), next(NULL), random(NULL) {}
* };
*/
class Solution {
public:
RandomListNode *copyRandomList(RandomListNode *head) {
RandomListNode *res=head;
if (head == NULL) return res;
RandomListNode *cur = head;
while (cur){
RandomListNode* tmp = new RandomListNode(cur->label);
tmp->next = cur->next;
cur->next = tmp;
cur = tmp->next;
}
cur = head;
while (cur){
if (cur->random)
cur->next->random = cur->random->next;
cur = cur->next->next;
}
cur = head;
res = head->next;
while (cur){
RandomListNode* p1 = cur->next->next;
if (p1){
cur->next->next = p1->next;
}
cur->next = p1;
cur = p1;
}
return res;
}
};
6. 把有序链表转换为高度平衡的二叉搜索树。
思路是从中点做根,进行转换,这样就需要geiMid函数,然后递归的转换即可。
class Solution {
public:
TreeNode *sortedListToBST(ListNode *head) {
if (head == NULL) return NULL;
<span style="white-space:pre"> </span>if(head->next == NULL) return new TreeNode(head->val);
ListNode* mid = getMid(head);
TreeNode* root = new TreeNode(mid->val);
root->left = sortedListToBST(head);
root->right = sortedListToBST(mid->next);
return root;
}
private:
ListNode * getMid(ListNode *head){
if (head == NULL || head->next == NULL) return NULL;
ListNode *slow = head, *fast = head->next;
while (fast->next && fast->next->next){
fast = fast->next->next;
slow = slow->next;
}
ListNode* res = slow->next;
slow->next = NULL;
return res;
}
};
7. 把链表m到n之间的节点进行反转。
思路:可以采用头插法。从第m个节点开始,将点m到n依次插入当段链表的头部。
class Solution {
public:
ListNode *reverseBetween(ListNode *head, int m, int n) {
if (head == NULL || m < 1 || m >= n) return head;
ListNode dummy(0); dummy.next = head;
ListNode* pre = &dummy, *cur = head;
for (int i = 0; i < m - 1; i++)
pre = pre->next;
if (pre)
cur = pre->next;//待插入的点是cur的下一个节点
for (int i = m; i < n; i++){
ListNode* tmp = cur->next;
cur->next = tmp->next;
tmp->next = pre->next;
pre->next = tmp;
//cur = cur->next;
}
return dummy.next;
}
};
8. 根据给定值分割链表,保持各个分割的相对顺序不变。
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.
For example,
Given1->4->3->2->5->2and x = 3,
return1->2->2->4->3->5.
两种思路:第一种类似快排的写法,值小就插入小的链表的尾部,值大就越过不处理。采用for循环写法
第二种用两个头指针分别记录比x值大的链表和比x值小的链表。
以下代码是第一种思路:
class Solution {
public:
ListNode *partition(ListNode *head, int x) {
ListNode tmpNode(0);
ListNode* dummy = &tmpNode;
dummy->next = head;
ListNode* p = dummy;
for (ListNode* cur = dummy; cur->next; ){
if (cur->next->val < x){
if (cur == p){
p = p->next;
cur = cur->next;
}
else{
ListNode* tmp = cur->next;
cur->next = tmp->next;
tmp->next = p->next;
p->next = tmp;
p = tmp;
}
}
else{
cur = cur->next;
}
}
return dummy->next;
}
};
9. 删除排序链表的中的重复元素,重复的只保存一份。
模仿数组,写一个for循环,如果跟前边的值一样,就删除。
class Solution {
public:
ListNode *deleteDuplicates(ListNode *head) {
if (head == NULL) return head;
for (ListNode* cur = head; cur->next;){
if (cur->next->val == cur->val){
ListNode* tmp = cur->next;
cur->next = tmp->next;
delete tmp;
}
else{
cur = cur->next;
}
}
return head;
}
};
10. 删除链表中的重复元素。如果出现重复,则全部删除不保留。
设置一个辅助项,然后遍历链表,如果cur->next 和cur->next->next的值相等,则把该值value记录下来,用一个while循环,只要值等于value的节点,删除。
class Solution {
public:
ListNode *deleteDuplicates(ListNode *head) {
if (head == NULL || head->next == NULL) return head;
ListNode tmpNode(0), *dummy = &tmpNode;
dummy->next = head;
ListNode* cur = dummy;
for (cur; cur->next&&cur->next->next;){
if (cur->next->val == cur->next->next->val){
ListNode* tmp = cur->next;
int val = tmp->val;
while (tmp && tmp->val == val){
ListNode* p = tmp;
tmp = tmp->next;
delete p;
}
cur->next = tmp;
}
else{
cur = cur->next;
}
}
//if (cur!=dummy && cur->next && cur->val == cur->next->val){
// delete cur->next;
// cur->next = NULL;
//}
return dummy->next;
}
};
添加一个辅助项,然后直接实现合并。
class Solution {
public:
ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) {
if (l1 == NULL || l2 == NULL)
return l1 ? l1 : l2;
ListNode tmpNode(INT_MIN), *dummy = &tmpNode, *cur = dummy;
while (l1 && l2){
if (l1->val < l2->val){
cur->next = l1;
l1 = l1->next;
cur = cur->next;
}
else{
cur->next = l2;
l2 = l2->next;
cur = cur->next;
}
}
cur->next = l1 ? l1 : l2;
return dummy->next;
}
};
12. 把链表尾部的k个结点旋转到头部。
第一种思路:首先更新k(防止大于length),然后用距离为k个节点的slow 和 fast指针,断开并反转。
第二种思路:遍历到链表尾部,然后记录链表的长度,并把链表串成环,然后在len-k处进行断开即可。
第一种思路:
class Solution {
public:
ListNode *rotateRight(ListNode *head, int k) {
if (head == NULL) return head;
int len = getLength(head);
k = k%len;
if (k == 0)
return head;
ListNode *slow = head, *fast = head;
while (fast && k--)
fast = fast->next;
while (fast->next){
fast = fast->next;
slow = slow->next;
}
ListNode* res = slow->next;
slow->next = NULL;
fast->next = head;
return res;
}
private:
int getLength(ListNode* head){
int count = 0;
while (head){
count++;
head = head->next;
}
return count;
}
};
第二种思路:
class Solution {
public:
ListNode *rotateRight(ListNode *head, int k) {
if (head == NULL) return head;
int len = 1;
ListNode* cur = head;
while(cur->next){
len++;
cur = cur->next;
}
cur->next = head;
k = k%len;
cur = head;
for(int i=0;i<len-k-1;i++){
cur = cur->next;
}
head = cur->next;
cur->next = NULL;
return head;
}
};
13. 把链表中每k个一组,进行反转。不足k个不反转。
首先计算链表的长度length,然后只要满足length>=k就进行一轮反转,然后length -=k.更新好每组的首尾指针即可。
class Solution {
public:
ListNode *reverseKGroup(ListNode *head, int k) {
int len = 0;
ListNode* temp = head;
while (temp){
len++;
temp = temp->next;
}
if (len < k)
return head;
ListNode dummy(0);
temp = &dummy;
while (len >= k){
ListNode* cur = head;
ListNode* pre = NULL;
ListNode* nex = NULL;
for (int i = 0; i < k; i++){
nex = cur->next;
cur->next = pre;
pre = cur;
cur = nex;
}
temp->next = pre;
temp = head;
head = cur;
len -= k;
}
temp->next = head;
return dummy.next;
}
};
14. 链表中的节点两两反转。此题是上一题的子集,不过因为是两两反转,所以可以考虑用递归实现,代码简洁。
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;
}
};
15. 合并k个有序链表。
合并k个有序链表或者k个有序数组的合并可以借助优先队列很巧妙的实现。先把各个链表的头节点加入优先队列,每次从头部取出当前层最小的节点,然后push一个最小的节点的下一个节点进入优先队列,如此循环。
class cmp{
public:
bool operator()(const ListNode* a, const ListNode* b){
return a->val > b->val;
}
};
class Solution {
public:
ListNode *mergeKLists(vector<ListNode *> &lists) {
if (lists.empty()) return NULL;
ListNode tmpNode(0), *dummy=&tmpNode, *cur = dummy;
priority_queue<ListNode*, vector<ListNode*>, cmp> myqueue;
for (int i = 0; i < lists.size(); i++){
if(lists[i])
myqueue.push(lists[i]);
}
while (!myqueue.empty()){
ListNode* tmp = myqueue.top();
myqueue.pop();
cur->next = tmp;
cur = tmp;
if (tmp->next)
myqueue.push(tmp->next);
}
return dummy->next;
}
};
16. 移除链表倒数第n个结点。
因为可能删除的节点是头节点,所以添加一个辅助节点,然后用一个距离为n的slow 和 fast指针,进行查找和删除。
class Solution {
public:
ListNode *removeNthFromEnd(ListNode *head, int n) {
ListNode tmpNode(0), *dummy = &tmpNode;
dummy->next = head;
ListNode *slow = dummy, *fast = dummy;
while (n--)
fast = fast->next;
while (fast->next){
slow = slow->next;
fast = fast->next;
}
ListNode* tmp = slow->next;
slow->next = tmp->next;
delete tmp;
return dummy->next;
}
};
17. 两个用链表表示的数字相加。
设置一个辅助项,循环相加,注意处理进位即可。
class Solution {
public:
ListNode *addTwoNumbers(ListNode *l1, ListNode *l2) {
if (l1 == NULL || l2 == NULL)
return l1 ? l1 : l2;
ListNode tmpNode(0), *dummy=&tmpNode, *cur=dummy;
int carry = 0;
while (l1 && l2){
int val = l1->val + l2->val + carry;
cur->next = new ListNode(val % 10);
carry = val / 10;
l1 = l1->next;
l2 = l2->next;
cur = cur->next;
}
ListNode* p = l1 ? l1 : l2;
while(p){
int val = p->val + carry;
cur->next = new ListNode(val % 10);
carry = val / 10;
p = p->next;
cur = cur->next;
}
if (carry){
cur->next = new ListNode(carry);
}
return dummy->next;
}
};