排序总结
时间复杂度 | 空间复杂度 | 是否能有稳定性 | |
选择 | 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;
}
};