数据结构 排序
插入排序
直接插入排序
基本思想:
每步将一个待排序的元素,插入到已经排好序的一组元素的适当位置上去,直到元素全部插入为止。
基本操作:
- 将待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列;
- 取出下一个元素,在已经排序的元素序列中从后向前扫描;
- 如果该元素(已排序)大于新元素,将该元素移到下一位置;
- 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置;
- 将新元素插入到该位置后;
- 重复步骤2~5。
图示:
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define x first
#define y second
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;
const int N = 10010;
int a[maxn];
void insertion_sort(int len) {
// 对每个元素
for (int i = 1; i < len; ++i) {
int key = a[i];
int j = i - 1;
// 插入合适的位置
while (j >= 0 && a[j] > key) {
a[j + 1] = a[j];
j--;
}
a[j + 1] = key;
}
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
//排序
insertion_sort(n);
//输出
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
return 0;
}
插入排序转自
希尔排序
思想:
对于n个元素的待排序的数列,
- 取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;
- 然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。
- 然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。
图示
特点:
- 一次移动移动位置大,跳远的接近排序后的最终位置。
- 增量序列必须递减,最后一个必须是1
- 增量序列应该是互质的
希尔排序是不稳定排序
希尔排序的时间复杂度
与增量(即,步长gap)的选取有关。例如,当增量为1时,希尔排序退化成了直接插入排序,此时的时间复杂度为O(N²),而Hibbard增量的希尔排序的时间复杂度为O(N3/2)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define x first
#define y second
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;
const int N = 10010;
int a[maxn];
int n;
void xisort(int x)
{
for (int i = x; i < n; i++)
{
for (int j = i - x; j >= 0; j -= x)
{
if (a[j] > a[j + x])
{
swap(a[j], a[j + x]);
}
}
}
}
int main()
{
while (cin >> n)
{
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
int dk = n / 2;
xisort(dk);
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
dk = 1;
xisort(dk);
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
}
return 0;
}
交换排序
冒泡排序
思想
基本思想
- 将序列当中的左右元素,依次比较,保证右边的元素始终大于左边的元素;
( 第一轮结束后,序列最后一个元素一定是当前序列的最大值;) - 对序列当中剩下的n-1个元素再次执行步骤1。
- 重复多次,直到有序。
时间复杂度
On2
辅助空间:
O1
稳定性
稳定的
代码
代码1:常用
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define x first
#define y second
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;
const int N = 10010;
int a[maxn];
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
//排序
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - 1 - i; j++)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
}
}
}
//输出
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
return 0;
}
代码2:抽出函数体
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define x first
#define y second
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;
const int N = 10010;
int a[maxn];
//一次冒泡
void bubble(int n){
for(int i=0;i<n-1;i++){
if(a[i]>a[i+1]){
swap(a[i],a[i+1]);
}
}
}
void bubbleSort(int n){
for(int i=n;i>=1;i--){
bubble(i);
}
}
int main()
{
int n;
cin>>n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
bubbleSort(n);
//输出
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
return 0;
}
快速排序
基本思想:
- 从序列当中选择一个基准数(pivot)
一般选择序列当中第一个数为基准数 - 将序列当中的所有数依次遍历,比基准数大的位于其右侧,比基准数小的位于其左侧,形成两个子表。
- 对子表重复步骤1.2,直到所有子集当中只有一个元素为止。
时间复杂度
平均时间最快。
越乱速度越快,基本有序的情况下On2。
快速排序不是原地排序
需要O logn的栈空间。
最快是O n
快速排序不是稳定排序
代码
注意:只能先进行while(i<j&&a[j]>=tmp)j--;
#include <bits/stdc++.h>
#define pii pair<int, int>
using namespace std;
int a[900000];
int n;
void q_sort(int l, int r)
{
if (l >= r)
return;
int tmp = a[l];
int i=l,j=r;
while(i<j){
while(i<j&&a[j]>=tmp)j--;
while(i<j&&a[i]<=tmp)i++;
swap(a[i],a[j]);
}
swap(a[l],a[i]);
// int mid=(l+r)/2;
q_sort(l, i - 1);
q_sort(i + 1, r);
}
int main()
{
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
q_sort(0, n - 1);
for (int i = 0; i < n; i++)
{
cout << a[i] << " ";
}
cout<<endl;
return 0;
}
选择排序
简单选择排序
基本思想:
在待排序的数据中选出最大(小)的元素放在其最终的位置。
基本操作:
- 首先通过n-1次关键字比较,从n个记录中找出关键字最小的记录,将 它与第一个记录交换
- 再通过n-2次比较,从剩余的n-1个记录中找出关键字次小的记录,将它与第二个记录交换
- 重复上述操作,共进行n-1趟排序后,排序结束
图示:
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define PII pair<int, int>
#define x first
#define y second
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
const ll M = 1e9 + 7;
const int N = 10010;
int a[maxn];
int findMaxPos(int n)
{
int maxNumber = a[0];
int maxid = 0;
for (int i = 0; i < n; i++)
{
if (a[i] > maxNumber)
{
maxNumber = a[i];
maxid = i;
}
}
return maxid;
}
void selectionSort(int n)
{
while (n > 1)
{
int maxid = findMaxPos(n);
swap(a[maxid], a[n - 1]);
n--;
}
}
int main()
{
int n;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
//排序
selectionSort(n);
//输出
for (int i = 0; i < n; i++)
{
if (i == n - 1)
cout << a[i] << endl;
else
cout << a[i] << " ";
}
return 0;
}