这几天把数据结构中的八大排序算法复习了一下,今天在这里做个小总结:
1.常见的排序算法
2,下面分别介绍以下几种排序算法以及他们的性能比较
冒泡排序
/** 1,冒泡排序 */
public static void BubbleSort(int[] arr){
for(int i=1;i<arr.length;i++) {
int flag=0;
for(int j=0;j<arr.length-i;j++){
int tmp=0;
if(arr[j]>arr[j+1]){
tmp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=tmp;
flag=1;
}
}
if(flag==0)break;
}
}
插入排序
基本思想:将待排序的元素依次插入到已排好序的队列中,直到所有元素插入完毕,则整个数列有序(刚开始的时候第一个元素有序)。
代码实现:
/** 2,插入排序 */
public static void InsertSort(int[] arr){
//外层循环:遍历待插入的所有数组元素
//内层循环:将外层循环本轮选择的元素与排好序的元素相比较
for(int i=1;i<arr.length;i++){
int tmp=arr[i]; //先将本次要插入的元素存起来
int j=0;
for(j=i-1;j>=0;j--) {
if (tmp < arr[j]) {
arr[j+1]=arr[j];
continue;
}
if(tmp>=arr[j]){
break;
}
}
arr[j+1]=tmp;
}
}
快速排序
/** 3,快速排序
* 思想:递归,分治
* */
public static void FastSort(int[] arr, int start,int end){
if(start>=end){
return;
}
//基准元素位置
int baseIndex=partition(arr,start,end);
FastSort(arr,start,baseIndex-1);
FastSort(arr,baseIndex+1,end);
}
private static int partition(int[] arr,int start,int end) {
int left = start;
int right = end;
int base = arr[start]; //将基准元素存起来
int pit = start; //坑的位置
while (left <= right) {
while (left <= right) {
if (arr[right] <= base) {
arr[left] = arr[right];
pit = right;
left++; //注意,填坑之后,指针要向前走
break;
}
right--;
}
while (left <= right) {
if (arr[left] >= base) {
arr[right] = arr[left];
pit = left;
right--;
break;
}
left++;
}
}
arr[pit]=base;
return pit;
}
选择排序
思想:每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在数列的起始位置,直到全部待排序的数据元素排完。
/** 4,选择排序 */
public static void ChooseSort(int[] arr){
for(int i=0;i<arr.length;i++){
int min=i;//注意:min不能一直取一个值,因为每层内循环结束后前面的已经是最小值了,若min不变,则无法再找出较小值
int tmp=arr[i];
for(int j=i+1;j<arr.length;j++){
if(arr[j]<arr[min]){
min=j;
}
}
arr[i]=arr[min];
arr[min]=tmp;
}
}
希尔排序
/** 5,希尔排序
* 思想:取一个整数gap(刚开始一般取arr.length/3+1)将待排序数组从第一个元素开始每隔gap个数分成一组,
* 共分n组,然后将每一组进行插排 ,一趟排序结束后,gap值减小(一般取gap=gap/3+1),继续上述步骤
* */
public static void ShellSort(int[] arr,int size){
int gap=size;
while(true) {
gap=gap/3+1;
shellInsert(arr,size,gap);
if(gap==1){
break;
}
}
}
//一次排序
private static void shellInsert(int[] arr,int size,int gap){
for(int x=0;x<gap;x++) {
for (int i = gap+x; i < arr.length; i += gap) {
int tmp = arr[i]; //先将本次要插入的元素存起来
int j = 0;
for (j = i - gap; j >= 0; j -= gap) {
if (tmp < arr[j]) {
arr[j + gap] = arr[j];
continue;
}
if (tmp >= arr[j]) {
break;
}
}
arr[j + gap] = tmp;
}
}
}
堆排序
堆排序的流程图如下:假设数组元素为arr[]={4,2,6,3,8,5,7,10,9},先将数组构建成一个大顶堆,再将根节点与最后一个叶子节点交换,完了之后将此叶子节点删除(用黑色表示),接着将堆向下调整为大顶堆,重复上述操作,直到所有节点删除完毕(注意:此处的删除不是真正的删除,只是将堆的节点个数减1,方便下次交换时交换到倒数第n个节点)。再将前其层序遍历即可。
最后再将其层序遍历得到
至此堆排序就完成了。
代码实现如下:
/** 6,堆排序
* 思想:将数组构建成大顶堆,取出最大元素与最后一个叶子节点交换之后删除,调整堆为大顶堆循环操作,最后层序遍历
* */
//堆排序
public static void HeapSort(int[] arr,int length){
BuildHeap(arr);
for(int i=length-1;i>0;i--){
int tmp=arr[0];
arr[0]=arr[i];
arr[i]=tmp;
DownAdjust(arr,0,i);
}
}
//构建堆
private static void BuildHeap(int[] arr){
for(int i=(arr.length-2)/2;i>=0;i--){
DownAdjust(arr,i,arr.length);
}
}
//向下调整
private static void DownAdjust(int[] arr,int index,int length){
int childIndex=index*2+1;
int tmp=arr[index];
while(childIndex<length){
if(childIndex+1<length && arr[childIndex]<arr[childIndex+1]){
childIndex=childIndex+1;
}
if(arr[childIndex]<=tmp) {
break;
}
arr[index]=arr[childIndex];
index=childIndex;
childIndex=index*2+1;
}
arr[index]=tmp; //注意,走到这一步时,若循环是正常结束, arr[index]已经是叶子节点了
}
归并排序
思想:将序列不断划分更小的子序列,当子序列长度为1时,再将相邻两个子序列排序合并,层层往上,最终使整个序列有序。
原理如图:
代码如下:
/** 7,归并排序
* 思想:分治法(先使每个子序列有序,再使子序列段间有序)
* */
//先分解
private static void resolve(int[] arr,int left,int right){
int mid=left+(right-left)/2;
if(left<right){
resolve(arr,left,mid);
resolve(arr,mid+1,right);
MergerSort(arr,mid,left,right);
}
}
//再合并
public static void MergerSort(int[] arr,int mid,int left,int right){
int[] brr=new int[right-left+1];
int i=left;
int j=mid+1;
int n=0;
//两个有序数组合成一个有序数组
while(i<=mid && j<=right){
if(arr[i]<=arr[j]){
brr[n]=arr[i];
i++;
n++;
}
if(arr[i]>arr[j]){
brr[n]=arr[j];
j++;
n++;
}
}
while(i<=mid){ //注意:等号不可没有
brr[n++]=arr[i++];
}
while(j<=right){
brr[n++]=arr[j++];
}
//新合成的有序数组覆盖掉原来的数组(如果不覆盖原数组将没变化)
for(int k=0;k<brr.length;k++){
arr[k+left]=brr[k];
}
}
计数排序
思想:假设数列为 a[]={9,3,5,4,9,1,2,7,8,1,3,6,5,3,4,0,10,9};
由于数列取值范围在0~10之间,因此可以开辟一个大小为11的数组B,B的下标对应a的数组元素,而a中某一元素的个数对应B数组对应下标的元素值,比如数组a中元素为9的共3个,则B[9]=3;最终遍历B数组打印B[i]个i即可;
代码如下:
/** 8,计数排序
* 思想:根据A数组元素的范围创建一个B数组,其下标值对应A数组的元素值 ,
* 遍历A数组将其对应的元素个数统计出来赋值给B数组下表对应的元素,
* 最后遍历B数组打印输出B[i]个i(可根据最大元素与最小元素的差进行改进)
* 适用:数组元素分布范围较小
* */
public static void BaseSort(int[] arr){
int max=0,min=arr[0];
for(int i=0;i<arr.length;i++){
if(arr[i]>max){
max=arr[i];
}
if(arr[i]<min){
min=arr[i];
}
}
int L=max-min;
int[] brr=new int[max-min+1];
for(int i=0;i<arr.length;i++){
brr[arr[i]-min]++;
}
for(int i=0;i<brr.length;i++){
for(int j=0;j<brr[i];j++){
System.out.print((i+min)+" ");
}
}
}
总结: