列举了12种考研排序
冒泡排序
- 比较相邻的元素,如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素作同样的工作,从第一对开始,一直到最后一对,做完后,最后的元素会是最大的元素。
- 针对所有的元素重复上面的步骤,除排序好的。
- 持续对越来越少的元素重复上述步骤,直到哪次没有任何一对数字需要比较或者是交换
void bubbleSort(int a[], int n)
{
for (int i = 0; i < n - 1; i++)
{
for (int j = 0; j < n - i - 1; ++j)
{
if (a[j] > a[j + 1])
{
swap(a[j], a[j + 1]);
}
}
}
}
选择排序
-
在未排序的序列中找到最小(大)元素,存放到排序序列的起始位置。
-
从剩下未排序元素中继续寻找最小(大)元素,然后放到自己已排序的序列的末尾。
-
以此类推,直到所有元素排序完毕
void selectionSort(int a[], int len)
{
int min;
for (int i = 0; i < len - 1; i++)
{
min = i;
for (int j = i + 1; j < len; j++)
{
if (a[j] < a[min])
{
min = j;
}
}
swap(a[i], a[min]);
}
}
插入排序
- 从第一个元素开始,该元素可以认为已经被排序。
- 取出下一个元素,在已经排序的元素序列中从后向前扫描。
- 如果该元素(已排序)大于新元素,该元素移到下一个位置。
- 重复步骤3,直到找到已排序的元素小于或者等于新的元素的位置。
- 将元素插入到对应位置。
- 重复2~5。
void InsertSort(int a[], int n)
{
for (int i = 1; i < n; i++)
{
if (a[i] < a[i - 1])
{
int val = a[i];
int j = i - 1;
a[j + 1] = a[j];
while (j > 0 && val < a[j - 1])
{
a[j] = a[j - 1];
j--;
}
a[j] = val;
}
}
}
快速排序
- 选第一个数为标准。
- 将比基准小的数据交换到前面,比基准大的交换到后面
- 对左边的空间和右边的空间重复,直到各区间只有一个数字
void QuickSort(int a[], int left, int right)
{
if (left >= right)
return;
int begin = left;
int end = right;
//keyi定在右边,需要从左边开始找
int keyi = end;
int key = a[keyi];
while (begin < end)
{
//因为我们从小往大排
//从左边开始找 找大
while (begin < end && a[begin] <= key)
begin++;
a[end] = a[begin];
//从右边开始找 找小
while (begin < end && a[end] >= key)
end--;
a[begin] = a[end];
}
a[end] = key;
QuickSort(a, left, end - 1);
QuickSort(a, end + 1, right);
}
堆排序
- 如果要从小到大排序,建立大堆,根节点大于左右子树。
- 将根结和最后一个元素交换,并且树的元素个数减1。
- 重复1~2,直到只剩一个元素。
void HeadAdjust( ElemType A[] , int k , int len){
//函数HeadAdjust将元素K为根的子树继续调整
A[0] = A[k]; //A[0]暂存子树的根节点
for(i = 2*k ; i <= len ; i*=2){ //沿key较大的子节点向下筛选
if( i < len && A[i] < A[i+1])
i++; //取key较大的子节点的下标
if(A[0] >= A[i])
break; //筛选结束
else{
A[k] = A[i]; //将A[i]调整到双亲结点
k = i; //修改k值,以便继续向下筛选
}
}
A[k] = A[0]; //被筛选节点的值放入最终位置
}
void BuildMaxHeap( ElemType A[] , int len){
for(int i = len/2 ; i>0 ; i-- )
HeadAdjust(A ,i , len); //从i = [n/2],反复调整堆
}
void HeapSort( ELemType A[] , int len){
//排序算法
BuildMaxHeap(A , len); //建立堆
for( i = len/2 ; i >0 ;i--){ //n-1趟的交换和键堆过程
Swap(A[i] , A[1]); //输出栈顶元素(和栈底交换)
HeadAdjust(A , 1 , i-1); //调整,把剩余的i-1个元素调整成堆
}
}
计数排序
- 找出待排序的数组最大和最小的元素
- 统计数组中每个值为i的元素出现的个数,存入数组c的第i-min项
- 将下标+min的值根据在数组c中的个数存到原数组中。
void CountSort(int a[], int len)
{
int max = a[0];
int min = a[0];
for (int i = 0; i < len; i++)
{
if (a[i] > max)
max = a[i];
if (a[i] < min)
min = a[i];
}
int l = max - min;//计算数组最大最小值的差
int* count_a = new int[l + 1];
for (int i = 0; i < l + 1; i++)
{
count_a[i] = 0;
}
for (int i = 0; i < len; i++)
{
count_a[a[i] - min]++;//统计元素个数
}
int j = 0;
for (int i = 0; i < len; i)
{
while (j <= l && count_a[j])
{
count_a[j]--;
a[i] = j + min;
i++;
}
j++;
}
}
桶排序
- 设置一个定量的数组当作空桶;
- 遍历输入数据,并且把数据一个一个放到对应的桶里去;
- 对每个不是空的桶进行排序;
- 从不是空的桶里把排好序的数据拼接起来。
基数排序
- 取得数组中的最大数,并取得位数。
- arr为原始数组,从最低位开始取每个位组成radix数组。
- 对radix进行计数排序(利用计数排序适用于小范围数的特点)。
希尔排序
- 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk = 1。
- 按照增量序列个数k,对序列进行k趟排序
- 每趟排序根据对应的增量ti,将待排序的序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。
void ShellSort(int* a, int n)
{
printf("原数组->", gap);
PrintArray(a, n);
int gap = n;//间隔
while (gap > 1)
{
gap = gap / 3 + 1;//保证最后一趟一定为1
for (int i = 0; i < n - gap; i++)
{
int end = 0;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
printf("gap:%d->", gap);
PrintArray(a, n);
}
}
归并排序
- 把长度为n的输入序列分成两个长度为n/2的子序列;
- 对这两个子序列分别采用归并排序;
- 将两个排序好的子序列合并成一个最终的排序序列。
ElemType *B=(ElemType *)malloc((n+1)*sizeof(ElemType)); //辅助数组B(动态分配内存)
void Merge(ElemType A[],int low,int mid,int high){
//表A的两段A[low…mid]和A[mid+1…high]各自有序,将它们合并成一个有序表
for(int k=low;k<=high;k++) B[k]=A[k];
//将A中所有元素复制到B中
for(int i=low,j=mid+1,k=i;i<=mid&&j<=high;k++){
//k是归并之后数组的下标计数器
if(B[i]<=B[j]) //比较B的左右两段中的元素
A[k]=B[i++]; //将较小值复制到A中
else
A[k]=B[j++];
}
while(i<=mid)
A[k++]=B[i++]; //若第一个表未检测完,直接将剩下的部分复制过来
while(j<=high)
A[k++]=B[j++]; //若第二个表未检测完,直接将剩下的部分复制过来
}
void MergeSort(ElemType A[],int low,int high){
if(low<high){
int mid=(low+high)/2;//从中间划分两个子序列
MergeSort(A,low,mid);//对左侧子序列进行递归排序
MergeSort(A,mid+1,high);//对右侧子序列进行递归排序
Merge(A,low,mid,high);//归并
}
}
折半插入排序
void InsertSort(ElemType A[] , int n){
int i , j ,high , mid , low;
for(i =2 ; i < n ;i++){
A[0] = A[i];
low = 1;
high = i-1;
while(low <= high){
mid = (low + high)/2;
if(A[mid] > A[0])
high = mid-1;
else
low = mid+1;
}
for( j = i-1 ; i>= high+1 ; --J)
A[j+1] = A[j];
A[high+1] = A[0];
}
}
简单排序
//顺序表形式
void SelectSort( ElemType A[] , int n ){
for( int i = 0 ; i < n ; i++){
min = i; //记录最小元素位置
for( j = i+1 ; j < n ; j++){ //从A[]里面找最小元素
if( A[j] < A[min])
min = j; //更新最小元素位置
}
if(min = !i )
Swap( A[i] , A[min]); //封装的swap函数一次移动3次元素
}
}
//链表结构
void selectSort_LinkList(LinkList &L){
Lnode *h = L , *p, *q , *s , *r;
while( p != NULL){
p = s = h ; //指针S和R记忆最大节点和其前驱,p为工作结点,q为前驱
q = r= NULL;
while( p != NULL){
if( p->data > s->data){
s = p;
r = q; //找到更大的,记忆他和它的前驱
}
q = p;
p = p->nexr; //继续寻找
}
if( s == h)
h = h->next; //最大节点在原链表前端
else
r->next = s->next;
s->next = L; //最大节点在原链表表内
L = s; //结点s插入结果链前端
}
}
算法
双向冒泡排序
从正反两个方向交替扫描
void BubbleSort(int A[] , int n){
//双向冒泡排序,交替进行正反两个方向的起泡过程
int low = 0 , hight = n-1;
int temp = 0 ;
int flag = true; //标记一趟的元素是否有被交换
while( low < hight && flag){
flag = false;
for( int i = low ; i < n ; i++){
if(A[i] > A[i+1]){ //从前面向后面冒泡
temp = A[i+1]; //发生逆序
A[i+1] = A[i];
A[i] = temp;
flag = true;
}
}
hight--; //更新上界
for( int j = hight ; j < n ; j--){
if(A[j] < A[j-1]){ //发生逆序
temp = A[j-1];
A[j-1] = A[j];
A[j] = temp; //交换
flag = true; //置flag
}
}
low++; //修改下界
}
}
奇数交换到偶数前面
表A基本有序,且顺序存储,每个元素都是不相同的整形元素
void Move(int A[] , int n){
//对基本有序的表A的奇偶数进行一次划分
int i = 0 , j = n -1 , temp = 0; //i表示左端偶数元素下标,j表示右端奇数元素下标
while( i < j){
while( i<j && A[i] % 2 != 0) //找到偶数函数
i++;
while( i < j && A[j] %2 != 1) //找到奇数函数
j--;
if( i < j ){ //交换元素
temp = A[i];
A[i] = A[j];
A[j] = temp;
}
i++;
j--;
}
}
随机枢纽值的快排算法:将第k个数放在它正确的位置
设有{k1,k2,k3,…,kn},现将kn放在他正确的位置上,且比较次数要求少于n的
思想:以kn为枢纽,对其进行一趟快速排序
int Partition_random(int A[] , int n ,int low , int high){
int rand_Index = low + rand()%(high - low+1); //求出随机值
swap(A[rand_Index] , A[low]); //将枢纽值交换到第一个元素
int pivot = A[low]; //将第一个值设为枢纽值
int i = low; //使得表A[low ...i]中的所有元素都小于pivot,初始为空表
for( int j = low +1 ; j <= high ; j++){ //从第二个元素开始寻找小于基准的元素
if(A[j] < pivot) //找到后,交换到前面
swap(pivot , A[j]);
}
swap(A[i] , A[low]); //将基准元素插入最终位置
return i; //返回基准元素位置
}
寻找第k个值
void kth_Elem( int A[] , int low , int high , int k){
int temp_low = low;
int temp_high = high;
int pivot = A[low];
while(low < high){
while( low < high && A[high] <= pivot)
A[low] = A[high];
high--;
while( low < high && A[low] >= pivot )
A[high] = A[low];
low++;
}
a[low] = pivot;
//以上就是快速排序的 部分
if( k == low)
return low;
if(low >k) //往后递归查找
return kth_Elem(A , temp_low , low-1 , k) ;
else
return kth_Elem(A , low+1 , temp_high , k);
}
两集合元素个数差最小值和元素和之差最小值
两个不相交的子集A1和A2,他们之间的元素差是| n1 - n2|,元素和之差是|S1 - S2|
int setPartition( int A[] , int n){
int flag = 1,low = 0, low0 = 0 , high = n-1 , high0 = n-1 , k = n/2;
int pivotkey;
int s1 = 0 , s2 = 0;
while(flag){
pivotkey = A[0]; //选择衢枢轴
while( low < high ){ //基于枢轴进行划分
while( low < high && A[high] >= pivotkey )
high --;
if( high != low)
A[low] = A[high];
while( low < high && A[low] <= pivotkey)
low ++;
if( high != low)
A[high] = A[low];
}
A[low] = pivotkey;
if( low == key) //分组完成,算法结束
flag = 0;
if( low > key){ //枢轴及之后的元素属于A2,继续对i之前的元素继续划分
low0 = ++low;
high = high0;
}
else{ //枢轴及之前的元素属于A1
high0 = --high;
low = low0;
}
}
for(1 = 0 ; 1 < key ; 1++){
s1 += A[i];
}
for( i = k ; i < n ; k++){
s2 += A[i];
}
return s2 - s1;
}
荷兰国旗问题
仅有红、白、绿三种颜色组成的色块,编写复杂度为o(n)的算法,使其有序排列成荷兰国旗
typedef enum[RED , WHITE , BLUE] color; //设置枚举数组
void Flag_Arrange( color a[] , int n){
int i =0 ,j =0 , k = n-1;
while( j <= k )
switch(a[j]){ //判断颜色
case RED : swap(a[i] , a[j]) ; i++; break;
case WHITE : j++ ; break;
case BLUE : swap(a[j] , a[k]); k--;
//蓝色,则和k交换
//这里没有j++语句以防止交换后a[j]仍然是蓝色的情况
}
}
判断数据序列是否构成小根堆
bool IsMinHeap( ElemType A[] , int len){
if(len%2 == 0){ //若是偶数,则有一个单分支节点
if( A[len/2] > A[len]) //判断单分支结点
return false;
for( i = len/2 - 1 ;i >=1 ; i--){
if( A[i] > A[2*i] || A[i] > A[2*i+1])
return false; //判断所有双分支节点
}
}
else{ //len是奇数,
for( i =len/2 ; i>= 1 ; i--){ //判断所有双分支节点
if( A[i] > A[2*i] || A[i] > A[2*i+1])
return false;
}
return true;
}
}