冒泡排序
相邻数据之间比较,(从小往大时,)大的以后移动,一次遍历交换多次。
void sort(string& s)//从小往大排
{
for (int i = 0;; i < s.length();i++)
{
bool falg = true;//标志位,如果没有改变,就已经全部有序
for (int j = 0; j < s.length() - 1 - i; j++)//放到后面
{
if (s[j] > s[j + 1])
{
swap(s[j], s[j + 1]);//交换
falg = false;
}
}
if (falg) break;//提前结束
}
}
选择排序
选择当前未排部分的最大(小)值放到最后面。未排部分减一。一次遍历,只有一次swap
void sort(string& s)//从小往大排
{
for (int i =s.length()-1;i>0;i--)
{
int k = 0;//记录当前最大的下标
for (int j = 0; j <= i; j++)//放到后面
{
if (s[k] < s[j])
{
k = j;//记录最大值下标
}
}
//走完这一趟,再交换
swap(s[k], s[i]);//这里的i已经是最后了
}
}
插入排序
分为两个部分,前面已排有序,遍历后面,找到合适的位置进行插入
void sort(string& s)//从小往大排,len是有序的个数
{
string a=s;//定义新的字符串,保存原来的字符串
// s[0] = a[0];//有序
int len=1;//第一个,就是有序的
while (len < s.length())
{
int i = 0, num = len;
while (i < len)//找得合适的位置
{
if (a[len] <s[i])
break;
i++;
}
while (num > i)//后移动
{
s[num] = s[num - 1];
num--;
}
s[i] = a[len];
len++;
}
}
希尔排序
先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录"基本有序"时,再对全体记录进行依次直接插入排序。
增量gap:下标间隔gap为一个组。
开始gap接近len/3;每次gap/3;直到gap==1.
void sort(string& s)//从小往大排,len是有序的个数
{
int gap=1;//跳过的间距
while (gap < s.length() / 3)
{
//找得最接近s.length()的值,防止gap%3==0
gap = gap * 3 + 1;
}
while (gap >= 1)//有得比较
{
for (int i = gap; i < s.length(); i++)
{
//从后面往前做,但是组分开做,冒泡
for (int j = i; j >= gap && s[j] < s[j - gap]; j -= gap)
{
swap(s[j], s[j - gap]);
}
}
gap=gap / 3;
}
}
归并排序
分而治之
两个子序列有序,对两个子序列进行有序合并。一个子序列分为两个,出口是一个序列里只有一个成员
- 递归
void sort(int *a, int left, int right)//从小往大排,len是有序的个数
{
int min = (left +right) / 2;
if (left >= right)
return;
sort(a, left, min);
sort(a, min + 1, right);
merge(a, left, min, right);
}
void merge(int* a, int left, int min, int right)
{
vector<int> b;//定义一个不定数组
int start1 = left, start2 = min + 1;
int end1 = min, end2 = right;
while (start1 <= end1 && start2 <= end2)
{
if (a[start1] <= a[start2])
{
b.push_back(a[start1]);//插入
start1++;
}
else
{
b.push_back(a[start2]);
start2++;
}
}
while (start1 <= end1)
{
b.push_back(a[start1]);//插入
start1++;
}
while (start2 <= end2)
{
b.push_back(a[start2]);//插入
start2++;
}
//赋值回去
for (int i = 0; i <b.size() ; i++)
{
a[left + i] = b[i];
}
}
- 迭代
template<typename T> // 整數或浮點數皆可使用,若要使用物件(class)時必須設定"小於"(<)的運算子功能
void merge_sort(T arr[], int len) {
T *a = arr;
T *b = new T[len];
//seg+=seg; *2
for (int seg = 1; seg < len; seg += seg) {
//start += seg + seg 相邻两个组之间做,越过两组
for (int start = 0; start < len; start += seg + seg) {
int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
T *temp = a;
a = b;
b = temp;
//现在a是交换好的数据
}
//新的数组有更改
if (a != arr) {
for (int i = 0; i < len; i++)
b[i] = a[i];
//现在b指向 arr,修改指针,指回原来new的空间
b = a;
}
delete[] b;
}
快速排序
分治法:分而治之
- 选择第一位为比较值,指针p,q从两端开始遍历,比惨,取到a[p]>a[0],a[q]<a[0],交换。
- 递归:分成两部分的无序,进行递归调用。
void sort(string &s,int start,int end)
{
if (start>=end) return;//出口,差值1是不排序的
int i = start+1, j = end;//第一个是比较的值
int key = s[start];
char c;
while (j>=i)
{
while (s[i] <= key)//这个i要交换
{
if (i == end)
break;//找到最后一个,不用更新
i++;
}
while (s[j]>=key)//这个j要交换
{
if (j == start)
break;
j--;
}
if (i >= j)break;//不用换
//swap(s[i], s[j]);//交换
c = s[i];
s[i] = s[j];
s[j] = c;
}//整完了
//这时候,j的位置就是要替换的位置
c = s[j];
s[j] = s[start];
s[start] = c;
sort(s, start,j-1 );
sort(s, j + 1, end);
}
堆排序
最大堆(用父节点比子节点大去排)
- 数组看成二叉树的结构,父节点a[i]的第一个子节点是a[2*i+1];
- 第一次:遍历整个数组,使得第一次的最大堆实现,每一个父节点都比子节点大。
- 往后每一次,从尾部未排的节点的父节点出发。
- 一次走完,交换头结点和尾部未排的节点
//获得当前start为最高父节点的最大堆。
void max_high(int arr[], int start, int end)
{
//开始父节点,和它的第一个节点
int dad = start, son = dad * 2 + 1;
while (son<=end)
{
if (son + 1 <= end && arr[son] < arr[son + 1])//就两个节点,选大的
son++;
if (arr[son] < arr[dad])
return;//不用做了,完美
else
{
//更新,指针后移,继续遍历
swap(arr[son], arr[dad]);
dad = son;
son = dad * 2 + 1;
}
}
}
//堆排序
void heap_sort(int arr[], int len)
{
//从最后一个父节点开始,每行 2*n。遍历每一个父节点,使他成为下面的最大堆
for (int i = len / 2 - 1; i >= 0; i--)
{
max_high(arr, i, len - 1);
}
//第一次做完,开始交换数据,最后一位做好了,继续下一次遍历
for (int i = len - 1; i >= 0; i--)
{
//交换
swap(arr[0], arr[i]);
//下一次开始
max_high(arr, 0, i-1);//这个i已经做好了,从头开始排了
}
}
//调用
int arr[]={12,42,5,7,94,3};
//获得数组长度,*arr==arr[0];
int len=sizeof(arr)/sizeof(*arr);
heap_sort(arr,len);
计数排序
- 取到最大值和最小值
- 建立一个数组,记录数字。第一个记录个数,第二个记录数字
- 遍历原数组,修改个数【下标减去small】
- 把数组放回去原来的
void CountSort(int a[], int len)
{
int i = 0;
//取到最大值和最小值
int small, big;
small = big = a[0];
while (i < len)
{
if (a[i] > big)
big = a[i];
if (a[i] < small)
small = a[i];
i++;
}
//建立一个数组,记录数字
vector <vector<int>>Arr;//第一个记录个数,第二个记录数字
vector<int> num;
i = small;
num.push_back(0);//把0放进去,初始化
while (i <= big)
{
num.push_back(i);
Arr.push_back(num);//放进去
num.pop_back();//把数字放出来
i++;
}
i = 0;
//遍历原数组,修改个数
while (i < len)
{
Arr[a[i] - small][0]++;
i++;
}
//把数组放回去原来的
i = 0;
int j = 0;
while (i < Arr.size() && j < len)
{
int ANum = Arr[i][0];
while (ANum > 0)
{
a[j] = Arr[i][1];
ANum--;
j++;
}
i++;
}
}
桶排序
桶排序是计数排序的升级版:先把数据分布在桶里面(eg两位数用十位判断入桶的位置),桶内数据采用别的排序方式进行排序
高效:
- (1)在额外空间充足的情况下,尽量增大桶的数量
- (2)使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
最快:当输入的数据可以均匀的分配到每一个桶中。
最慢:当输入的数据被分配到了同一个桶中。
基数排序
一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。
eg:按十位入桶,出桶,按个位入桶,出桶,完成
- 基数排序:根据键值的每位数字来分配桶;
- 计数排序:每个桶只存储单一键值;
- 桶排序:每个桶存储一定范围的数值;