目录
搜索
1、顺序搜索
- 适用于所有数组
int linearSearch(vector<int>& nums, int target) {
for (int i = 0; i < nums.size(); i++) {
if (nums[i] == target) return i;
}
return -1;
}
2、二分搜索
- 只适用于排好序的数组
int binarySearch(vector<int>& nums, int target) {
int left = 0, right = nums.size() - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (nums[mid] == target) return mid;
if (nums[mid] > target) right = mid - 1;
else left = mid + 1;
}
return -1;
}
排序
1、冒泡排序
- LB三人组之一------O(n^2)
// 1-1、冒泡排序
vector<int> bobbleSort(vector<int>& nums) {
for (int i = 0; i < nums.size() - 1; i++) { // n-1趟
for (int j = 0; j < nums.size() - 1 - i; j++) { // 未排好序区间
if (nums[j] > nums[j + 1]) {
int tmp = nums[j]; // 交换
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
return nums;
}
// 1-2、冒泡排序改进:一趟未交换说明已经排好序了
vector<int> bobbleSort1(vector<int>& nums) {
for (int i = 0; i < nums.size() - 1; i++) {
bool exchange = false; // 标志位
for (int j = 0; j < nums.size() - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
int tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
exchange = true; // 交换了标志位改变
}
}
if (!exchange) return nums;
}
return nums;
}
2、选择排序
- 每次选择最小值排序
- LB三人组之一------O(n^2)
// 2-1、选择排序(非原地排序)
vector<int> selectSort(vector<int>& nums) {
vector<int> res;
for (int i = 0; i < nums.size(); i++) {
int index = 0, minVal = nums[0];
for (int j = 1; j < nums.size(); j++) { // 获得最小值
if (nums[j] < minVal) {
index = j;
minVal = nums[j];
}
}
res.push_back(minVal);
nums[index] = 1000; // 最小值位置用一个较大数覆盖
}
return res;
}
// 2-2、选择排序改进(原地排序)
vector<int> selectSort1(vector<int>& nums) {
for (int i = 0; i < nums.size() - 1; i++) {
int index = i;
for (int j = i + 1; j < nums.size(); j++) {
if (nums[index] > nums[j]) index = j; // 确定最小值索引
}
if (index != i) { // 不是原位置才需交换
int tmp = nums[i];
nums[i] = nums[index];
nums[index] = tmp;
}
}
return nums;
}
3、插入排序
- 每次选数插入到适合位置,即已经选好的数排好序
- LB三人组之一------O(n^2)
// 3、插入排序
vector<int> insertSort(vector<int>& nums) {
for (int i = 1; i < nums.size(); i++) {
int tmp = nums[i]; // 摸到的数
int index = i - 1; // 手里数下标
while (index >= 0 && nums[index] > tmp) {
nums[index + 1] = nums[index]; // 右移
index--;
}
nums[index + 1] = tmp; // 找到适合位置放入
}
return nums;
}
4、快速排序
- 选取一个数,使这个数归位
- 即数组被分为两部分,左边都比这个数小,右边都比这个数大
- 递归完成排序
- NB三人组之一------O(n*log(n))
// 4、快速排序
int partition(vector<int>& nums, int left, int right) {
int tmp = nums[left]; // 选取的数
while (left < right) {
while (left < right && nums[right] >= tmp) right--;
nums[left] = nums[right]; // 小数放左边
while (left < right && nums[left] <= tmp) left++;
nums[right] = nums[left]; // 大数放右边
}
nums[left] = tmp; // 选取的数放回数组
return left;
}
vector<int> quickSort(vector<int>& nums, int left, int right) {
if (left < right) {
int mid = partition(nums, left, right); // 归位函数
quickSort(nums, left, mid - 1);
quickSort(nums, mid + 1, right);
}
return nums;
}
5、堆排序
- 建堆---农村包围城市(从最后一个孩子节点的父节点开始一步步使得子树有序)
- 得到堆顶元素为最大值
- 去掉堆顶,将最后一个元素置于堆顶,通过一次向下调整使堆有序
- 堆顶为第二大元素
- 重复步骤3
- NB三人组之一------O(n*log(n))
// 5、堆排序
// 大根堆------父节点的值大于左右孩子节点的值
void siftLarge(vector<int>& nums, int low, int high) {
int i = low; // 根节点
int j = 2 * i + 1; // 左孩子节点
int tmp = nums[low]; // 堆顶元素
while (j <= high) {
if (j + 1 <= high && nums[j] < nums[j + 1]) j++; // 右孩子大
if (nums[j] > tmp) {
nums[i] = nums[j];
i = j;
j = 2 * i + 1;
}
else {
nums[i] = tmp; // 找到适合位置放入
break;
}
}
nums[i] = tmp; // 放在叶子节点
}
vector<int> heapSort(vector<int>& nums) {
int n = nums.size() - 1;
for (int i = (n - 1) / 2; i >= 0; i--) { // 建堆
siftLarge(nums, i, n);
}
for (int i = n; i >= 0; i--) {
int tmp = nums[0]; // 交换堆顶和最后一个元素
nums[0] = nums[i];
nums[i] = tmp;
siftLarge(nums, 0, i-1);
}
return nums;
}
堆排序应用------topk问题
- 排序后切片 O(nlogn)
- LB三人组 O(kn)
- 堆排序思想 O(nlogk)
- 选取k个元素建立小根堆------堆顶就是第k大元素
- 每次从剩下元素中选取一个元素,if<堆顶,忽略;else置于堆顶并向下调整
- 倒序弹出堆顶
// 堆排序应用---topk问题
// 小根堆------父节点的值小于左右孩子节点的值
void siftSmall(vector<int>& nums, int low, int high) {
int i = low; // 根节点
int j = 2 * i + 1; // 左孩子节点
int tmp = nums[low]; // 堆顶元素
while (j <= high) {
if (j + 1 <= high && nums[j] > nums[j + 1]) j++; // 右孩子小
if (nums[j] < tmp) {
nums[i] = nums[j];
i = j;
j = 2 * i + 1;
}
else {
nums[i] = tmp; // 找到适合位置放入
break;
}
}
nums[i] = tmp; // 放在叶子节点
}
vector<int> topk(vector<int>& nums, int k) {
vector<int> heap;
for (int i = 0; i < k; i++) heap.push_back(nums[i]); // 取前k个元素
for (int i = (k - 2) / 2; i >= 0; i--) siftSmall(heap, i, k - 1); // 建小跟堆
for (int i = k; i < nums.size(); i++) {
if (nums[i] > heap[0]) { // 大于堆顶元素替换并向下调整
heap[0] = nums[i];
siftSmall(heap, 0, k - 1);
}
}
for (int i = k-1; i >= 0; i--) {
int tmp = heap[0]; // 交换堆顶和最后一个元素
heap[0] = heap[i];
heap[i] = tmp;
siftSmall(heap, 0, i - 1);
}
return heap;
}
6、归并排序
- 分解------将数组越分越小,直到分成一个元素
- 终止条件------一个元素是有序的
- 合并------将元素数组合并,越来越大
- NB三人组之一------O(n*log(n))
// 6、归并排序
void merge(vector<int>& nums, int left, int mid, int right) {
vector<int> ltmp;
int i = left;
int j = mid + 1;
while (i <= mid && j <= right) { // 两边都有数
if (nums[i] < nums[j]) {
ltmp.push_back(nums[i]);
i++;
}
else {
ltmp.push_back(nums[j]);
j++;
}
}
while (i <= mid) { // 一边没数
ltmp.push_back(nums[i]);
i++;
}
while (j <= right) {
ltmp.push_back(nums[j]);
j++;
}
int index = 0; // 更新nusm
for (int k = left; k <= right; k++) {
nums[k] = ltmp[index];
index++;
}
}
vector<int> mergeSort(vector<int>& nums, int left, int right) {
if (left < right) {
int mid = left + (right - left) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
merge(nums, left, mid, right);
}
return nums;
}