5.快速排序
快速排序(Quicksort)是对冒泡排序的一种改进。基本思想是:通过一趟排序将要排序的数据分割成独立的两
部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排
序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。`
重点:
(1)在于需要选一个中轴值pivot,将数组分成左右两组,在分组的过程中,使用双指针分别从头和尾向中轴值pivot遍历,找到位置再一起交换,直到两个指针错位,结束遍历,完成分组。
(2)arr[l] == pivot 和 arr[r] == pivot时将左指针后移,右指针迁移,直到左右指针错位退出循环,防止一直原值交换死循环
(3)l=r,两个指针同时指向同一个数,也会原值交换,死循环,需要r–, l++,错开
public static void quickSort (int[]arr,int left,int right) {
int l = left;//左下标,一般传入0,
int r = right;//右下标,一般传入arr.length-1
//pivot中轴值
int pivot = arr[(left + right) / 2];
int temp = 0;//临时变量,作为交换时用
//while循环的目的是让比pivot值小的放到左边
//让比pivot大的值放到右边
while (l < r) {
//第一步:双指针先定位,定好需要交换的位置
//在pivot的左边一直找,找到大于等于pivot值时才退出
while (arr[l] < pivot) {
l += 1;
}
//在pivot的右边一直找,找到小于等于pivot值时才退出
while (arr[r] > pivot) {
r -= 1;
}
//如果l >= r说明pivot的左右两边的值,已经按照左边全小于等于pivot,右边全大于等于pivot排好了
if (l >= r) {
break;
}
//第二步:交换
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
//交换完后判断,若arr[l] == pivot,则arr[r] == pivot
// r和l会一直交换最外层的while死循环;所以要每交换一次,前移r, 后移l,直到l>=r,跳出循环
if (arr[l] == pivot) {
r -= 1;
}
if (arr[r] == pivot){
l += 1;
}
}
//第三步:分组后分别开始左右递归
if (l==r){//l=r,说明两个都指向pivot,两个一直原值交换
l+=1;
r-=1;//将r和l错开,防止递归时栈溢出
}
//至此,已经r<l了,r,l 错开
//向左递归,
if (left < r){//r指针已经交换到pivot的前一个位置了
quickSort(arr,left,r);
}
//向右递归
if (right > l){//l指针已经交换到pivot的后一个位置了
quickSort(arr,l,right);
}
}
6.归并排序
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)
策略。分,即将待排序数组分成两个数组(这个分并非真的有两个数组,只是通过指针划分出来的,类似“挡板”),逐渐递归分组直到一个元素为一组;治,将分得的组(两个有序数组)合并成一个有序数组,逐渐递归往上,最后合并成需要的有序数组,完成排序。
关键在于治,如何将两个有序数组合并成一个有序数组?
重点:
(1)需要一个temp数组存放要合并的两个有序序列数据;对两个有序序列分别从头开始遍历,并比较,将较小值依次放入temp
(2)将temp拷贝到arr[ ]数组
public static void main(String[] args) {
int[] arr={8,4,5,7,1,3,6,2};
int[] temp = new int[arr.length];//归并排序需要额外的空间
mergeSort(arr,0,arr.length-1,temp);
System.out.println("归并排序后"+ Arrays.toString(arr));
}
//分+合
public static void mergeSort(int[] arr,int left,int right,int[] temp){
if (left < right){
int mid = (left+right)/2;//中间索引
//向左递归分解
mergeSort(arr,left,mid,temp);
//向右递归分解
mergeSort(arr,mid+1,right,temp);
//合并
merge(arr,left,mid,right,temp);
}
}
/**
*
* @param arr 排序的原始数组
* @param left 左边有序序列的初始索引
* @param mid 中间索引
* @param right 右边有序序列索引的末位索引
* @param temp 中转的数组
*/
public static void merge(int[] arr,int left,int mid,int right,int[] temp){
int i=left;//初始化i,左边有序序列的初始索引
int j=mid+1;//初始化j,右边有序序列的初始索引
int t=0;//指向temp数组的当前索引
//1.先把左右两边(有序)的数组按规则填充到temp数组
//直到左右两边的有序序列,有一边处理完毕
while (i<=mid && j<=right){
if (arr[i] < arr[j]){
temp[t] = arr[i];//将较小的arr[i]填到temp数组
t+=1;
i+=1;
}else {//反之
temp[t] = arr[j];//将较小的arr[j]填到temp数组
t+=1;
j+=1;
}
}
//2.把有剩余数据的一边的数据依次全部填充到temp
while (i <= mid){//左边有剩余
temp[t]=arr[i];
t+=1;
i+=1;
}
while (j <= right){//右边有剩余
temp[t]=arr[j];
t+=1;
j+=1;
}
//3.将temp数组拷贝到arr,并不是每一次都拷贝所有
t=0;//指针从新指向数组首位
int tempLeft=left;
//第一次:tempLeft=0,right=1;
//第二次:tempLeft=2,right=3
//第三次:tempLeft=0,right=3
//最后次:tempLeft=0,right=7
System.out.println("tempLeft="+tempLeft+",right="+right);
while (tempLeft <= right){
arr[tempLeft] = temp[t];
t += 1;
tempLeft += 1;
}
}
`7.基数排序
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort),它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用。将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
重点:
(1)获取待排序数组中的最大位数
(2)构造一个二维数组10*arr.length, 10位桶的个数,每一个桶分别代表0-9这10个数,每个数放到哪个桶需要通过计算:arr[j] /n % 10,操作个位时n=1,十位n=10,百位n=100,依次。。。。。。
(3)需要bucketElementCounts数组对每个桶中的元素个数进行统计,并在每一轮排序将桶中元素取出放回到原数组后清0,
public static void radixSort(int[] arr){
//得到数组arr[]中最大的数的位数
int max = arr[0];//假设第一个数就是最大数
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max){
max = arr [i];
}
}
//得到最大是几位数
int maxLength = (max + "").length();
//二维数组包含10个一维数组,每个一维数组就是一个桶
//为防止栈溢出(最坏情况个位数相同),一维数组设为arr.length
//基数排序是空间换时间的经典算法
int[][] bucket = new int [10][arr.length];
//为记录每个桶中每轮实际存放数据个数,再定义一个一维数组
//bucketElementCounts[0],记录桶bucket[0]放入的数据,以此类推
int[] bucketElementCounts = new int[10];
for (int i = 0, n=1; i < maxLength; i++,n *=10) {
//第i轮,针对每个元素对应的位(个n=1,十n=10,百n=100,千n=1000。。。)
for (int j = 0; j <arr.length ; j++) {
//取出每个元素的对应位的值
int digitOfElement = arr[j] /n % 10;
//当前数放入对应桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement] ++;
}
//按照这个桶的顺序(一维数组的下标一次取出数据,放入原来的数组)
int index = 0;//原数组arr【】的索引
//遍历每一个桶,并将桶中数据放入到原数组。
for (int k = 0; k <bucketElementCounts.length ; k++) {
//如果桶中有数据,才放入到原数组
if (bucketElementCounts[k] !=0){
//遍历该桶,将该第k个桶里的所有数据(l个数),依次放入原数组
for (int l = 0; l < bucketElementCounts[k]; l++) {
arr[index ++] = bucket[k][l];
}
}
//第i+1轮处理后,需要把每一个bucketElementCounts[k]=0
bucketElementCounts[k]=0;
}
}
}