1、冒泡排序
思路:每次迭代都将最大的那个放到最后,而下一次迭代时只需要关注前n-1个,再将这里面的最大的放到最后。
代码:
void BubbleSort(vector<int>& nums)
{
int n = nums.size();
for (int i = 0; i < n-1; i++)
{
for (int j = 0; j < n - i - 1; j++)
{
if (nums[j] > nums[j + 1])
swap(nums[j], nums[j + 1]);
}
}
}
复杂度分析:
平均时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
最优时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
最差时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
稳定
2、快速排序
思路:先任选一个数据(通常第一个),找到这个数据在数组中的位置(左右两个指针,分别把比他小的放左边,比他大的放右边,要保证左指针经过的都是比他小的,右指针经过的都是比他大的),然后对左右两部分迭代刚刚的步骤。
代码:
// 快速排序-,让后比他小的,放到左边,比他大的放到右边,迭代此过程
int Partition(vector<int>& nums, int low, int high) // 把支点的位置排好
{
int pivot = nums[low]; // 先找到一个基准元素(随便找,拿第一个元素开始吧)
// 此时low位置空缺,因此从high指针开始找比他小的元素
while (low < high) // 一直到重合
{
while (low<high && nums[high]>=pivot) // 直到找到比他小的
high--;
nums[low] = nums[high];
while (low < high && nums[low] <= pivot)
low++;
nums[high] = nums[low];
// 此时说明在low下面的都比他小,在high上面的都比他大
}
nums[low] = pivot;
return low; // 返回的就是我们选择的基准元素在后面排好的表中的位置
}
void QuickSort(vector<int>& nums, int low, int high)
{
if (low < high)
{
int pivotpos = Partition(nums, low, high); // 基准元素的位置已经固定
QuickSort(nums, low, pivotpos-1);
QuickSort(nums, pivotpos+1, high);
}
}
复杂度分析:
平均时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
最优时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
最差时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:平均/最优
O
(
log
n
)
O(\log n)
O(logn)、最差
O
(
n
)
O(n)
O(n)
不稳定:稳定性通俗地讲就是能保证排序前2个相等的数其在序列的前后位置顺序和排序后它们两个的前后位置顺序相同
3、归并排序
思路:迭代二分序列,直到左右已经有序(只有一个元素则已经有序),然后合并两个有序序列。
代码:
void merge(vector<int>& nums, int low, int mid, int high)
{ // 这里都假设两个子序列分别已经有序,则合并两个有序序列
int p1 = low, p2 = mid + 1, p3 = 0; // p3在合并后新数组的索引
vector<int> temp(high - low + 1, 0);
while (p1 <= mid && p2 <= high)
{
if (nums[p1] <= nums[p2])
temp[p3++] = nums[p1++];
else
temp[p3++] = nums[p2++];
}
while (p1 <= mid)
temp[p3++] = nums[p1++];
while (p2 <= high)
temp[p3++] = nums[p2++];
for (int i = 0; i < high - low + 1; i++)
nums[low + i] = temp[i];
}
void mergeSort(vector<int>& nums, int low, int high) // 归并排序:思想:迭代二分,归并左右
{
if (low == high) return;
int mid = low + (high - low) / 2;
mergeSort(nums, low, mid);
mergeSort(nums, mid + 1, high);
merge(nums, low, mid, high); //合并
}
复杂度分析:
平均时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
最优时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
最差时间复杂度:
O
(
n
log
n
)
O(n\log n)
O(nlogn)
空间复杂度:
O
(
n
)
O(n)
O(n)
稳定
4、选择排序
思路:每次将待排序元素中最小的一个与在排序的开头的一个交换
代码:
void SelectSort(vector<int>& nums)
{
int n = nums.size();
for (int i = 0; i < n-1; i++)
{
int min = i;
for (int j = i; j < n; j++)
if (nums[j] < nums[min])
min = j;
swap(nums[i], nums[min]);
}
}
复杂度分析:
平均时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
最优时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
最差时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
不稳定
5、插入排序
思路:假定第一个已经有序,后面只有找到比前一个小的元素,就将其取出,然后向回判断只要比他大,就把那个元素往后移动一个
代码:
void InsertSort(vector<int>& nums)
{
int n = nums.size();
for (int i = 1; i < n; ++i) // 假定第一个已经有序
{
if (nums[i] < nums[i - 1]) // 只要比前一个小
{
int x = nums[i]; // 取出这一个
int j = i - 1; // 标记前一个的位置
while (j >= 0 && x < nums[j])
{
nums[j + 1] = nums[j];
j--;
}
nums[j+1] = x;
}
}
}
复杂度分析:
平均时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
最优时间复杂度:
O
(
n
)
O(n)
O(n)
最差时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
稳定
6、希尔排序
思路:希尔排序又称“缩小增量”排序,希尔排序是把记录按下标的一定增量分组,对每组使用插入排序,随着增量减少,每组包含的关键词增多,希尔排序比较相隔远距离的数,使得数移动时能够跨过多个元素,则一次比较能够消除多个元素交换。
void ShellSort(vector<int>& nums)
{
int n = nums.size();
for (int r = n / 2; r >= 1; r = r / 2)
{
for (int i = r; i < n; i = i + r) // i从r开始,也就是假定第0个已经有序
{
int temp = nums[i]; // 取出哨兵元素
int pre_index = i - r; // 前一个增量位置
while (pre_index >= 0 && temp < nums[pre_index])
{
nums[pre_index + r] = nums[pre_index];
pre_index = pre_index-r;
}
nums[pre_index + r] = temp;
}
}
}
复杂度分析:
平均时间复杂度:
O
(
n
1.3
−
2
)
O(n^{1.3-2})
O(n1.3−2)
最优时间复杂度:
O
(
n
)
O(n)
O(n)
最差时间复杂度:
O
(
n
2
)
O(n^2)
O(n2)
空间复杂度:
O
(
1
)
O(1)
O(1)
不稳定