排序

  1. 冒泡排序

基本思想:第一次循环将最大的元素放在arr[n-1],第二次循环将第二大的元素放在arr[n-2],最多需要n-1次循环

void bubble_sort(vector<int> &nums) {
    int n = nums.size();
    bool swapped;
    for (int i = 1; i < n; ++i) {
        swapped = false;
        for (int j = 0; j < n - i; ++j) {
            if (nums[j] > nums[j + 1]) {
                nums[j] = nums[j] ^ nums[j + 1];
                nums[j + 1] = nums[j] ^ nums[j + 1];
                nums[j] = nums[j] ^ nums[j + 1];
                swapped = true;
            }
        }
        if (!swapped) break; // 说明此时数组已有序,无需执行后续的循环
    }
}
  1. 选择排序

基本思想:第一次循环后将最小的元素放在arr[0],第二次循环后将第二小的元素放在arr[1],总共需要n-1次循环

void selection_sort(vector<int> &nums) {
    int n = nums.size(), min;
    for (int i = 0; i < n - 1; ++i) {
        min = i;
        for (int j = i + 1; j < n; ++j) {
            if (a[j] < a[min]) {
                min = j;
            }
        }
        if (min != i) {
            nums[i] = nums[i] ^ nums[min];
            nums[min] = nums[i] ^ nums[min];
            nums[i] = nums[i] ^ nums[min];
        }
    }
}
  1. 快速排序

基本思想:每次遍历时将比base小的元素放在左边,比base大的元素放在右边,然后递归遍历两个子数组。

void quick_sort(vector<int> &nums, int l, int r) {
    if (l >= r)return;
    int base = nums[l], start = l, end = r;
    while (l < r) {
        while (r > l && nums[r] >= base) {
            r--;
        }
        nums[l] = nums[r];
        while (l < r && nums[l] <= base) {
            l++;
        }
        nums[r] = nums[l];
    }
    nums[l] = base; // 此时nums[l]左边的数都小于base, 右边的数都大于base
    quick_sort(nums, start, l - 1);
    quick_sort(nums, l + 1, end);
}
  1. 归并排序

基本思想:将一个数组分割成两个有序的子数组,然后将其合并

void merge_sort(vector<int> &nums, int l, int r) {
    if (l >= r) return;
    int mid = (l + r) / 2;
    merge_sort(nums, l, mid); //对左半子序列排序
    merge_sort(nums, mid + 1, r); //对右半子序列排序
    mergeTwoSortedArr(nums, l, r, mid); //合并两个有序的子序列
}
void mergeTwoSortedArr(vector<int> &nums, int l, int r, int mid) {
    vector<int> tmp(r - l + 1);
    int i = l, j = mid + 1, index = 0;
    while (i <= mid || j <= r) {
        if (j > r || i <= mid && nums[i] < nums[j]) {
            tmp[index++] = nums[i++];
        } else {
            tmp[index++] = nums[j++];
        }
    }
    for (int i = 0; i < tmp.size(); ++i) {
        nums[l + i] = tmp[i];
    }
}
  1. 插入排序

基本思想:每次都将一个元素插入前面已经排好序的序列中

void insertion_sort(vector<int> &nums) {
    int tmp, j;
    for (int i = 1; i < nums.size(); ++i) {
        if (nums[i] >= nums[i - 1])continue;
        tmp = nums[i];
        for (j = i - 1; j >= 0 && nums[j] > tmp; --j) {
            nums[j + 1] = nums[j];
        }
        nums[j + 1] = tmp;
    }
}
  1. 堆排序

基本思想:将数组构造成大顶堆,每次取出根节点最为最大元素放在数组的最后

void heap_sort(vector<int> &nums) {
    int size = nums.size();
    //将数组转化成一颗完全二叉树,每个父节点和左子节点、右子节点的索引关系为 left=2*a+1;right=2*a+2
    //最后一个非叶子节点的索引是size/2 - 1
    for (int i = size / 2 - 1; i >= 0; --i) {
        maxHeapify(nums, i, size - 1); // 构造大顶堆,从最后一个非叶子节点开始往前遍历,直到将整个数组构造成一个大顶堆
    }
    for (int i = 1; i < size; ++i) {
        //此时nums[0]即为整个数组中的最大值,将其放在数组结尾
        swap(nums[0], nums[size - i]);
        maxHeapify(nums, 0, size - 1 - i); // 根节点变了,重新构造大顶堆,只需调整一次即可
    }
}

// 构造大顶堆, rootIndex为根节点下标, lastIndex为最后一个节点下标
void maxHeapify(vector<int> &nums, int rootIndex, int lastIndex) {
    int left = 2 * rootIndex + 1, right = 2 * rootIndex + 2, largestIndex = rootIndex;
    if (left <= lastIndex && nums[left] > nums[largestIndex]) {
        largestIndex = left;
    }
    if (right <= lastIndex && nums[right] > nums[largestIndex]) {
        largestIndex = right;
    }
    if (largestIndex != rootIndex) {
        // 交换nums[largestIndex]和nums[rootIndex]
        swap(nums[largestIndex], nums[rootIndex]);
        //交换后可能会破坏原有的大顶堆,因此需要继续调整
        maxHeapify(nums, largestIndex, lastIndex);
    }
}
  1. 数组中的第K个最大元素

解法1:优先队列

int findKthLargest(vector<int>& nums, int k) {
    priority_queue<int> pq;
    for (auto& num : nums) {
        pq.push(num);
    }
    for (int i = 0; i < k - 1; ++i) {
        pq.pop();
    }
    return pq.top();
}

解法2:参考堆排序

int findKthLargest(vector<int> &nums, int k) {
    int n = nums.size();
    for (int i = n / 2 - 1; i >= 0; --i) {
        maxHeapify(nums, i, n - 1);
    }
    for (int i = 0; i < k; ++i) { // 无需遍历n-1次,只需遍历k次
        swap(nums[0], nums[n - i - 1]);
        maxHeapify(nums, 0, n - i - 2);
    }
    return nums[n - k];
}

void maxHeapify(vector<int> &nums, int rootIndex, int lastIndex) {
    int left = 2 * rootIndex + 1, right = 2 * rootIndex + 2, largestIndex = rootIndex;
    if (left <= lastIndex && nums[left] > nums[largestIndex]) {
        largestIndex = left;
    }
    if (right <= lastIndex && nums[right] > nums[largestIndex]) {
        largestIndex = right;
    }
    if (largestIndex != rootIndex) {
        swap(nums[rootIndex], nums[largestIndex]);
        maxHeapify(nums, largestIndex, lastIndex);
    }
}

解法3:参考快速排序

int findKthLargest(vector<int> &nums, int k) {
    return quickSelection(nums, 0, nums.size() - 1, nums.size() - k);
}
// 寻找将nums[start]-nums[end]排好序后下标为target的元素
int quickSelection(vector<int> &nums, int start, int end, int target) {
    int idx = start + rand() % (end - start + 1);
    swap(nums[idx], nums[start]);
    int left = start, right = end, base = nums[start]; // base是每次进行快速排序时所使用的基准数,通过随机的方式来提高性能(也可以删除前面两行)
    while (left < right) {
        while (left < right && nums[right] >= base) {
            --right;
        }
        nums[left] = nums[right];
        while (left < right && nums[left] <= base) {
            ++left;
        }
        nums[right] = nums[left];
    }
    nums[left] = base;
    if (left == target) {
        return base;
    } else if (left > target) {
        return quickSelection(nums, start, left - 1, target);
    } else {
        return quickSelection(nums, left + 1, end, target);
    }
}

解法4:将数组分为大于base/小于base/等于base的3个集合,然后根据集合的元素个数来判断第k大的数处在哪个集合中

int findKthLargest(vector<int>& nums, int k) {
    int base= nums[0]; // base可以随机取,base = nums[rand()%nums.size()]
    vector<int> bigger, smaller, equal;
    for (auto& num : nums) {
        if (num > base) {
            bigger.push_back(num);
        } else if (num < base) {
            smaller.push_back(num);
        } else {
            equal.push_back(num);
        }
    }
    if (bigger.size() >= k) {
        return findKthLargest(bigger, k);
    } else if (k > nums.size() - smaller.size()) {
        return findKthLargest(smaller, k - nums.size() + smaller.size());
    }
    return base;
}
  1. 前 K 个高频元素

解法1:暴力求解,构造元素及其出现次数的映射集合,然后转成vector,按照出现次数从大到小排序,然后取出前k个

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> m;
    for (auto& num : nums) {
        m[num]++;
    }
    vector<pair<int, int>> v(m.begin(), m.end());
    sort(v.begin(), v.end(),
         [&](const pair<int, int>& a, const pair<int, int>& b) {
             return a.second > b.second;
         });
    vector<int> res;
    for (int i = 0; i < k; ++i) {
        res.push_back(v[i].first);
    }
    return res;
}

解法2:桶排序,用buckets[i]保存nums中出现了i次的元素。从后往前遍历bucket,取出前k个元素

vector<int> topKFrequent(vector<int>& nums, int k) {
    unordered_map<int, int> m; // 保存nums中元素及其出现次数
    int maxCount = 0; // 记录出现频率最高是多少次, 以便确定bucket数组的长度
    for (auto& num : nums) {
        maxCount = max(maxCount, ++m[num]);
    }
    vector<vector<int>> bucket(maxCount +
                               1); // bucket[i]保存所有出现了i次的元素
    for (auto& p : m) {
        bucket[p.second].push_back(p.first);
    }
    vector<int> ans;
    for (int i = maxCount; i >= 0; --i) {
        for (auto& x : bucket[i]) {
            ans.push_back(x);
            if (ans.size() == k)
                return ans;
        }
    }
    return ans;
}
  1. 根据字符出现频率排序

参考347. 前 K 个高频元素的解法2:桶排序

string frequencySort(string s) {
    unordered_map<char, int> count;
    int maxCount = 0;
    for (auto& c : s) {
        maxCount = max(maxCount, ++count[c]);
    }
    vector<vector<char>> buckets(maxCount + 1);
    for (auto& p : count) {
        buckets[p.second].push_back(p.first);
    }
    string res = "";
    for (int i = maxCount; i >= 0; --i) {
        for (auto& c : buckets[i]) {
            res.append(i, c);
        }
    }
    return res;
}
  1. 颜色分类

解法1:单指针

void sortColors(vector<int>& nums) {
    int ptr = 0;
    //第一次遍历后,[0,ptr-1]全部为0
    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] == 0) {
            nums[i] = nums[ptr];
            nums[ptr++] = 0;
        }
    }
    //第二次从ptr开始遍历,把1全部放到前面
    for (int i = ptr; i < nums.size(); ++i) {
        if (nums[i] == 1) {
            nums[i] = nums[ptr];
            nums[ptr++] = 1;
        }
    }
}

解法2:双指针,一次遍历

void sortColors(vector<int> &nums) {
    int p0 = 0, p1 = 0; // p0指向所有0的下一个元素, p1指向所有1的下一个元素
    for (int i = 0; i < nums.size(); ++i) {
        if (nums[i] == 0) {
            nums[i] = nums[p0];
            nums[p0] = 0;
            if (p0 < p1) { // 说明之前p0处的元素是1, 并且被换到了i处, 所以要将这个1换回来
                nums[i] = nums[p1];
                nums[p1] = 1;
            }
            ++p0;
            ++p1;
        } else if (nums[i] == 1) {
            nums[i] = nums[p1];
            nums[p1] = 1;
            ++p1;
        }
    }
}

解法3:双指针

void sortColors(vector<int>& nums) {
    int p0 = 0, p2 = nums.size() - 1;
    for (int i = 0; i <= p2; ++i) {
        while (i <= p2 && nums[i] == 2) {
            nums[i] = nums[p2];
            nums[p2] = 2;
            --p2;
        }
        if (nums[i] == 0) {
            nums[i] = nums[p0];
            nums[p0] = 0;
            ++p0;
        }
    }
}
posted @ 2025-07-17 12:28  dengkang1122  阅读(3)  评论(0)    收藏  举报