排序总结


时间复杂度

空间复杂度

是否能有稳定性

选择

O(N*N)

O(1)

×

冒泡

O(N*N)

O(1)

✔️

插入

O(N*N)

O(1)

✔️

归并

O(N*logN)

O(N)

✔️

快排(一般指3.0)

O(N*logN)

O(N*logN)

×

O(N*logN)

O(1)

×

基数排序作为不基于比较的排序,有稳定性

基础类型的排序一般排序用快排,因为其时间复杂度常数项更小,需要保持稳定定用归并,不想占用额外空间用堆排序

非基础类型的排序可以用归并,因为有稳定性

希尔排序:多轮插入排序

排序优化:

样本N较大时,先用快排划分为一个个N较小的样本,在小样本上使用插入排序(插入排序时间复杂度常数项极低)

哈希表查找 O(1)   有序表查找 O(logN)

笔试:不需要考虑空间复杂度

面试:空间复杂度尽量小

链表

基础题:

反转单向链表(leetcode 206)

/**
 * 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* reverseList(ListNode* head) {
        if(head == nullptr || head -> next == nullptr){
            return head;
        }
        ListNode *pre = head;
        ListNode *cur = head -> next;
        ListNode *later = head -> next -> next;
        pre -> next = nullptr;
        while(later){
            cur -> next = pre;
            pre = cur;
            cur = later;
            later = later -> next;
        }
        cur -> next = pre;
        return cur;
    }
};

反转双向链表

struct ListNode {
    int val;
    ListNode *next;
    ListNode *last;
    ListNode(int x) : val(x), next(nullptr), last(nullptr) {}
};

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == nullptr || head -> next == nullptr){
            return head;
        }
        ListNode *cur = head;
        ListNode *later = head -> next;
        ListNode *pre = nullptr;
        while(later){
            cur -> last = later;
            cur -> next = pre;
            pre = cur;
            cur = later;
            later = later -> next;
        }
        cur -> last = later;
        cur -> next = pre;
        return cur;
    }
};

  打印两链表公共部分

判断回文链表

笔试做法:快慢指针找到链表中间(链表长度为奇数/偶数 code不一样),栈

class Solution {
public:
    bool isPalindrome(ListNode *head){
        stack<ListNode*> stack;
        ListNode *cur = head;
        while(cur != nullptr){
            stack.push(cur);
            cur = cur -> next;
        }
        while(head != nullptr){
            // stack.pop()返回void,stack.top()返回栈顶元素
            if(head -> val != stack.top() -> val){
                return false;
            }
            head = head -> next;
            stack.pop();
        }
        return true;
    }
};

面试做法:快慢指针找到链表中间,右半部分链表反转,判断回文,恢复链表

class Solution {
public:
    bool isPalindrome(ListNode *head){
        if(head == nullptr || head -> next == nullptr){
            return true;
        }
        ListNode *n1 = head;
        ListNode *n2 = head;
        while(n2 -> next != nullptr && n2 -> next -> next != nullptr){
            n1 = n1 -> next;
            n2 = n2 -> next -> next;
        }
        n2 = n1 -> next;
        n1 -> next = nullptr;
        ListNode *n3 = nullptr;
        while(n2){
            n3 = n2 -> next;
            n2 -> next = n1;
            n1 = n2;
            n2 = n3;
        }
        n3 = n1;
        n2 = n1;
        n1 = head;
        bool res = true;
        while(n1 && n2){
            if(n1 -> val != n2 -> val){
                res = false;
                break;
            }
            n1 = n1 -> next;
            n2 = n2 -> next;
        }
        // 恢复反转的链表
        n2 = n3 -> next;
        n3 -> next = nullptr;
        while(n2){
            n1 = n2 -> next;
            n2 -> next = n3;
            n3 = n2;
            n2 = n1;
        }
        return res;
    }
};

将单向链表按某值划分成左边小,中间相等,右边大的形式

笔试做法:每个节点放在数组里,数组做partition,把节点串起来

void swap(vector<ListNode*>& nodes, int i, int j){
    ListNode *temp = nodes[i];
    nodes[i] = nodes[j];
    nodes[j] = temp;
}

class Solution {
public:
    ListNode* partition(ListNode* head, int k){
        vector<ListNode*> nodes;
        ListNode *cur = head;
        while(cur){
            nodes.push_back(cur);
            cur = cur -> next;
        }
        int less = -1;
        int more = nodes.size();
        int i = 0;
        while(i < more){
            if(nodes[i] -> val < k){
                swap(nodes, ++less, i++);
            }
            else if(nodes[i] -> val == k){
                i++;
            }
            else{
                swap(nodes, --more, i);
            }
        }
        for(i = 0 ; i < nodes.size() - 1; i++){
            nodes[i] -> next = nodes[i + 1];
        }
        nodes[i] -> next = nullptr;
        return nodes[0];
    }
};

面试做法:六个变量,小于/等于/大于区域的头/尾,最后连起来(要注意三个区域是否为空)

class Solution {
public:
    ListNode* partition(ListNode* head, int k){
        ListNode *less_head = nullptr;
        ListNode *less_tail = nullptr;
        ListNode *equal_head = nullptr;
        ListNode *equal_tail = nullptr;
        ListNode *more_head = nullptr;
        ListNode *more_tail = nullptr;
        while(head != nullptr){
            ListNode *later = head -> next;
            head -> next = nullptr;
            if(head -> val < k){
                if(less_head == nullptr){
                    less_head = head;
                    less_tail = head;
                }
                else{
                    less_tail -> next = head;
                    less_tail = head;
                }
            }
            else if(head -> val == k){
                if(equal_head == nullptr){
                    equal_head = head;
                    equal_tail = head;
                }
                else{
                    equal_tail -> next = head;
                    equal_tail = head;
                }
            }
            else{
                if(more_head == nullptr){
                    more_head = head;
                    more_tail = head;
                }
                else{
                    more_tail -> next = head;
                    more_tail = head;
                }
            }
            head = later;
        }
        ListNode *res = more_head;
        if(equal_head != nullptr){
            res = equal_head;
        }
        if(less_head != nullptr){
            res = less_head;
        }
        if(less_head != nullptr && more_head != nullptr){
            less_tail -> next = more_head;
        }
        if(less_head != nullptr && equal_head != nullptr){
            less_tail -> next = equal_head;
        }
        if(equal_head != nullptr && more_head != nullptr){
            equal_tail -> next = more_head;
        }
        return res;
    }
};

复制链表,链表节点除有next指针外,还有一个rand指针,随机指向链表某个节点或为空

笔试做法:哈希表(key:老节点 value:新节点)

class Solution {
public:
    ListNode* copyListWithRand(ListNode *head){
        unordered_map<ListNode*, ListNode*> mp;
        ListNode *cur = head;
        while(cur){
            mp.insert(make_pair(cur, new ListNode(cur -> val)));
            cur = cur -> next;
        }
        cur = head;
        while(cur){
            mp[cur] -> next = mp[cur -> next];
            mp[cur] -> rand = mp[cur -> rand];
            cur = cur -> next;
        }
        return mp[head];
    }
};

面试节点:把新节点串在老节点后面(再后面是下一个新节点),省掉哈希表

struct ListNode {
    int val;
    ListNode *next;
    ListNode *rand;
    ListNode(int x) : val(x), next(nullptr), rand(nullptr) {}
};

class Solution {
public:
    ListNode *copyListWithRand(ListNode *head){
        if(head == nullptr){
            return nullptr;
        }
        ListNode *cur = head;
        ListNode *later = nullptr;
        while(cur){
            later = cur -> next;
            cur -> next = new ListNode(cur -> val);
            cur -> next -> next = later;
            cur = later;
        }
        cur = head;
        while(cur){
            cur -> next -> rand = (cur -> rand == nullptr) ? nullptr : cur -> rand -> next;
            cur = cur -> next -> next;
        }
        cur = head;
        ListNode *res = head -> next;
        ListNode *copy_cur = head -> next;
        ListNode *copy_later = nullptr;
        while(cur && copy_cur){
            later = cur -> next -> next;
            copy_later = (copy_cur -> next == nullptr) ? nullptr : copy_cur -> next -> next;
            cur -> next = later;
            cur = later;
            copy_cur -> next = copy_later;
            copy_cur = copy_later;
        }
        return res;
    }
};