0
点赞
收藏
分享

微信扫一扫

数据结构和算法刷题总结(持续更新)

常见算法总结

1. 排序算法

1.1 选择排序

时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性:不稳定

void SelectSort(vector<int>& nums)
{
for (int i = 0; i < nums.size(); ++i)
{
int minIndex = i;
for (int j = i + 1; j < nums.size(); ++j)
minIndex = (nums[minIndex] > nums[j]) ? j : minIndex;
::swap(nums[minIndex], nums[i]);
}
}

1.2 冒泡排序

时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性:稳定

void BubleSort(vector<int>& nums)
{
for (int i = nums.size() - 1; i > 0; --i)
{
for (int j = 0; j < i; ++j)
{
if (nums[j] > nums[j + 1])
::swap(nums[j], nums[j + 1]);
}
}
}

1.3 插入排序

时间复杂度:O(n^2) 空间复杂度:O(1) 稳定性:稳定

void InsertSort(vector<int>& nums)
{
if (nums.size() <= 1)
return;
for (int i = 1; i < nums.size(); ++i)
{
for (int j = i; j > 0 && nums[j] < nums[j - 1]; --j)
::swap(nums[j], nums[j - 1]);
}
}

1.4 归并排序

时间复杂度:O(nlogn) 空间复杂度:O(n) 稳定性:稳定

void MergeSort(vector<int>& nums)
{
if (nums.size() <= 1)
return;
MergeSort_Process(nums, 0, nums.size() - 1);
}
void MergeSort_Process(vector<int>& nums, int l, int r)
{
if (l >= r)
return;
int mid = l + ((r - l) >> 1);
MergeSort_Process(nums, l, mid);
MergeSort_Process(nums, mid + 1, r);
Merge(nums, l, r, mid);
}
void Merge(vector<int>& nums, int l, int r, int mid)
{
int i = l, j = mid + 1;
vector<int> temp(r - l + 1);
int k = 0;
while (i <= mid && j <= r)
{
if (nums[i] <= nums[j])
temp[k++] = nums[i++];
else
temp[k++] = nums[j++];
}
while (i <= mid)
temp[k++] = nums[i++];
while (j <= r)
temp[k++] = nums[j++];
::copy(temp.begin(), temp.end(), nums.begin() + l);
}

1.5 快速排序

时间复杂度:O(nlogn) 空间复杂度:O(logn) 稳定性:不稳定

void QuickSort(vector<int>& nums)
{
if (nums.size() <= 1)
return;
srand((unsigned int)time(NULL));
QuickSort_Process(nums, 0, nums.size() - 1);
}
void QuickSort_Process(vector<int>& nums, int l, int r)
{
if (l >= r)
return;
int pos = l + (rand() % (r - l + 1));
int sr = 0, br = 0;
Partition(nums, l, r, sr, br, pos);
QuickSort_Process(nums, l, sr);
QuickSort_Process(nums, br, r);
}
void Partition(vector<int>& nums, int l, int r, int& sr, int& br, int pos)
{
int val = nums[pos];
sr = l - 1, br = r + 1;
int i = l;
while (i < br)
{
if (nums[i] < val)
::swap(nums[i++], nums[++sr]);
else if (nums[i] == val)
++i;
else
::swap(nums[i], nums[--br]);
}
}

1.6 堆排序

时间复杂度:O(nlogn) 空间复杂度:O(1) 稳定性:不稳定

void Heapify(vector<int>& nums, int heapSize, int i = 0)
{
int left = 2 * i + 1, right = left + 1;
while (left < heapSize)
{
int maxIndex = (right < heapSize && nums[right] > nums[left]) ? right : left;
maxIndex = (nums[maxIndex] < nums[i]) ? i : maxIndex;
if (i == maxIndex)
return;
::swap(nums[i], nums[maxIndex]);
i = maxIndex;
left = 2 * i + 1;
right = left + 1;
}
}
void HeapSort(vector<int>& nums)
{
if (nums.size() <= 1)
return;

int heapSize = nums.size();
for (int i = heapSize - 1; i >= 0; --i)
Heapify(nums, heapSize, i);

while (heapSize > 0)
{
::swap(nums[0], nums[--heapSize]);
Heapify(nums, heapSize);
}
}

1.7 基数排序

时间复杂度:O(n * k) 空间复杂度:O(n) 稳定性:稳定 (k为最大元素位数)

// 基数排序
int GetMaxDigit(const vector<int>& nums)
{
int maxNum = ::max_element(nums.begin(), nums.end());
int res = 0;
while (maxNum != 0)
{
maxNum /= 10;
++res;
}
return res;
}
int GetDigit(int num, int d)
{
while (d > 1)
{
--d;
num /= 10;
}
return num % 10;
}
void RadixSort(vector<int>& nums)
{
if (nums.size() <= 1)
return;
int d = GetMaxDigit(nums);
vector<queue<int>> buckets(10, queue<int>());

for (int i = 0; i < d; ++i)
{
int index = 0;
for (int j = 0; j < nums.size(); ++j)
buckets[GetDigit(nums[j], i + 1)].push(nums[j]);
for (int j = 0; j < 10; ++j)
{
while (!buckets[j].empty())
{
nums[index++] = buckets[j].front();
buckets[j].pop();
}
}
}
}

2. 链表

技巧一:dummyHead

创建头结点指向首元结点,以减少针对于首元节点的大量特殊情况判断

  • 合并链表

ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode dummyHead;
ListNode* cur = &dummyHead, *p = list1, *q = list2;
while (p != nullptr && q != nullptr)
{
if (p->val <= q->val)
{
cur->next = p;
p = p->next;
}
else
{
cur->next = q;
q = q->next;
}
cur = cur->next;
}
if (p != nullptr)
cur->next = p;
if (q != nullptr)
cur->next = q;
return dummyHead.next;
}

  • 分割链表(链表partition)

ListNode* partition(ListNode* head, int x) {
if (head == nullptr)
return nullptr;
ListNode lessDummy, // 存放小于x的链表
greaterDummy; // 存放大于等于x的链表
ListNode* p1 = &lessDummy, *p2 = &greaterDummy;
ListNode* p = head;
while (p != nullptr)
{
if (p->val < x)
{
p1->next = p;
p1 = p1->next;
}
else
{
p2->next = p;
p2 = p2->next;
}
// 断开原链表的连接
ListNode* temp = p->next;
p->next = nullptr;
p = temp;
}
p1->next = greaterDummy.next;
return lessDummy.next;
}

技巧二:双指针(快慢指针)

快指针先走,慢指针后走(针对部分情况快指针一次走两步,慢指针一次走一步)

  • 删除链表倒数第k个结点

ListNode* removeNthFromEnd(ListNode* head, int n) {
if (head == nullptr || n <= 0)
return head;
ListNode* fast = head, *slow = head;
int count = 0;
while (count < n)
{
++count;
fast = fast->next;
}
if (fast == nullptr) // 倒数第n个结点
{
ListNode* res = head->next;
delete head;
head = nullptr;
return res;
}
while (fast->next != nullptr)
{
fast = fast->next;
slow = slow->next;
}
ListNode* pNext= slow->next;
slow->next = pNext->next;
delete pNext;
pNext = nullptr;

return head;
}

  • 链表中点

ListNode* middleNode(ListNode* head) {
if (head == nullptr)
return nullptr;
if (head->next == nullptr)
return head;
ListNode* fast = head, *slow = head;
while (fast != nullptr && fast->next != nullptr)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}

  • 链表入环结点

ListNode *detectCycle(ListNode *head) {
if (head == nullptr || head->next == nullptr)
return nullptr;

ListNode* fast = head, *slow = head;
do
{
if (fast == nullptr || fast->next == nullptr)
return nullptr;
fast = fast->next->next;
slow = slow->next;
} while (fast != slow);

slow = head;
while (slow != fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}

技巧三:哈希表

用哈希表记录结点的对应关系,但空间复杂度会提高

  • 链表的相交结点

// 1. 哈希表法
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
unordered_set<ListNode*> record;
while (headA != nullptr)
{
record.insert(headA);
headA = headA->next;
}
while (headB != nullptr)
{
if (record.count(headB) != 0)
return headB;
headB = headB->next;
}
return nullptr;
}

// 2. 双指针法
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if (headA == nullptr || headB == nullptr)
return nullptr;
ListNode* p = headA, *q = headB;
int lenA = 0, lenB = 0;
while (p->next != nullptr)
{
p = p->next;
++lenA;
}
while (q->next != nullptr)
{
q = q->next;
++lenB;
}

// 无相交
if (p != q)
return nullptr;

int delta = abs(lenA - lenB);
p = (lenA > lenB) ? headA : headB;
q = (p == headA) ? headB : headA;

// 长的那根链表先走
while (delta > 0)
{
--delta;
p = p->next;
}
// 然后再一起走
while (p != q)
{
p = p->next;
q = q->next;
}
return p;
}

经典题目
  • 翻转链表

// 方法一:三指针法
ListNode* reverseList(ListNode* head) {
if (head == nullptr)
return nullptr;
ListNode* pPre = head, *pCur = head->next, *pNext;
pPre->next = nullptr;
while (pCur != nullptr)
{
pNext = pCur->next;
pCur->next = pPre;
pPre = pCur;
pCur = pNext;
}
return pPre;
}

// 方法二:头插法
ListNode* reverseList(ListNode* head) {
if (head == nullptr)
return nullptr;

ListNode* tail = head;
while (tail->next != nullptr)
tail = tail->next;

ListNode* p = head, *pNext;
while (p != tail)
{
pNext = p->next;
p->next = tail->next;
tail->next = p;
p = pNext;
}

return tail;
}

// 方法三:递归
ListNode* reverseList(ListNode* head) {
if (head == nullptr || head->next == nullptr)
return head;

ListNode* nextList = reverseList(head->next);
head->next->next = head;
head->next = nullptr;
return nextList;
}

举报

相关推荐

0 条评论