排序
一、内部排序
1.1 插入排序
1.1.1直接插入排序
void InsertSort(int A[],int n)
{
int i,j,temp;
for(i=1;i<n;i++) //假设第一个元素是已经排好序的,A[0],则从A[1]开始遍历
{
temp=A[i];
for(j=i-1;j>=0&&A[j]>temp;--j) //for循环内逻辑:先初始化——>判断条件——>进入循环体——>结束再自加
A[j+1]=A[j];
A[j+1]=temp;
}
}
1.1.2 希尔排序
主要是基于在直接排序中,如果序列有序,移动次数就会减少很多。希尔排序的思想:先追求表中元素部分有序,再逐渐逼近全局有序
算法思想:将待排序表分割成若干形如L[i,i+d,i+2d,…,i+kd]的“特殊”子表,堆各个子表分别进行直接插入排序,缩小增量d,重复上述过程,直到d=1为止。
void ShellSort(int A[],int n)
{
int d,i,j;
//A[0]只是暂存单元,不是哨兵,当j<=0时,插入为止已到
for(d=n/2;i<=n;d=d/2) //步长变化
{
for(i=d+1;i=n;i++)
{
if(A[i]<A[i-d]) //需将A[i]插入有序增量表
{
A[0]=A[i];
for(j=i-d;j>0&&A[0]<A[j];j-=d)
A[j+d]=A[j]; //记录后移,查找插入的位置
A[j+d]=A[0]; //插入
}//if
}
}
}
1.2 交换排序
1.2.1 冒泡排序
算法思想:从后往前(或从前往后)两两比较相邻元素的值,若为逆序( 即A[ i-1 ] > A[ i ] ),则交换它们,直到序列比较完整。
void bubleSort(int arr[],int n)
{
int i,j,flag;
int temp;
for(i=n-1;i>=1;i--) //外层循环来指示当前无序序列的范围
{
flag=0;
for(j=1;j<=i;j++)
{
if(arr[j-1]>arr[j])
{
temp=arr[j];
arr[j]=arr[j-1];
arr[j-1]=temp;
flag=1;
}
if(flag==0)
return;
}
}
}
1.2.2 快速排序
算法思想:运用到了分治的思想。
//用第一个元素将待排序序列划分成左右两个部分
int Partition(int A[],int low,int high)
{
int pivot=A[low]; //第一个元素作为枢轴
while(low<high) //用low、high搜索枢轴的最终位置
{
while(low<high&&A[high]>=pivot)
--high;
A[low]=A[high]; //比较枢轴小的元素移动到左端
while(low<high&&A[low]<=pivot)
++low;
A[high]=A[low]; //比枢轴大的元素移动到右端
}
A[low]=pivot; //枢轴元素存放到最终位置
return low; //返回存放枢轴的最终位置
}
//快速排序
void QuickSort(int A[],int low,int high)
{
if(low<high) //递归跳出的条件
{
int pivotpos=Partition(A,low,high); //划分
QuickSort(A,low,pivotops-1); //划分左子表
QuickSort(A,pivotpos+1,high); //划分右子表
}
}
1.3 选择排序
选择排序:每一趟在待排序元素中选取关键字最小(或最大)的元素加入有序序列
1.3.1 简单选择排序
算法思想:假设排序表为L[1…n],第i趟排序即从L[i…n]中选择关键字最小的元素与L(i)交换,每一趟排序可以确定一个元素的最终位置,这样经过n-1趟排序就可以使得整个排序表有序。
void selectSort(int arr[],int n)
{
int i,j,k;
int temp;
for(i=0;i<n;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(arr[k]>arr[j])
k=j;
}
temp=arr[i];
arr[i]=arr[j];
arr[k]=temp;
}
}
1.3.2 堆排序
1.3.2.1 大根堆
大根堆:完全二叉树中,根>=左、右
思路:把所有非终端结点都检查一遍,是否满足大根堆的要求,如果不满足,则进行调整。在顺序存储的完全二叉树中,非终端结点编号i<=[n/2](向下取整)
- 检查当前结点是否满足根>=左、右。若不满足,将当前结点与更大的一个孩子呼唤。
- 若元素互换破坏了下一级的堆,则采用相同的方式继续往下调整**(小元素不断“下坠”)**
- i的左孩子 ——2i
- i的右孩子 ——2i+1
- i的父节点 ——[ i/2 ]
建立大根堆
//建立大根堆
void BuildMaxHeap(int A[],int len)
{
for(int i=len/2;i>0;i--) //从后往前调整所有非终端结点
headAdjust(A,i,len);
}
//将以K为根的子树调整为大根堆
void HeadAdjust(int A[],int k,int len)
{
A[0]=A[k]; //A[0]暂存子树的根结点
for(int i=2*k;i<=len;i*=2) //沿kay较大的子结点向下筛选
{
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;
}
}
A[k]=A[0]; //被筛选结点的值放入最终位置
}
基于大根堆进行排序
选择排序:每一趟在待排序元素中选取关键字最大的元素加入有序子序列
堆排序:每一趟将堆顶元素加入有序序列(与待排序序列中的最后一个元素交换)
小根堆:完全二叉树中,根<=左、右