java排序算法 sort_排序算法总结(Java实现)

排序算法分类

排序算法根据处理数据使用到的存储设备可分为两大类,分别是内部排序和外部排序。内部排序:将需要处理的数据都加载到内存中进行排序

外部排序:由于数据量过于庞大,单靠内存无法完成,需要借助外部存储进行排序

内部排序又可细分,如下图所示

c4e421e0de2740ac1ca96bbd9c24e3f0.png

插入排序

1.直接插入排序基本思路:首先先从数组中选择一个数x放到一个数组里,从遍历当前数组,和新数组的每一个元素进行比较,从而决定它放在什么位置,只能当前数组的每个元素都放进去了

举个例子,数组{7,5,9,3,12,8,20,18},总共需要7趟,假设我们选择7作为第一个数

初始状态:(7),5,9,3,12,8,20,18,从下标1开始找,放到新数组里(7)

第1趟:(5,7),9,3,12,8,20,18

第2趟:(5,7,9),3,12,8,20,18

第3趟:(3,5,7,9),12,8,20,18

第4趟:(3,5,7,9,12),8,20,18

第5趟:(3,5,7,8,9,12),20,18

第6趟:(3,5,7,8,9,12,20),18

第7趟:(3,5,7,8,9,12,18,20)代码如下:public static void insertSort(int[] arr){

if (arr == null || arr.length <= 0 ) return;

int len = arr.length;

for (int i = 1; i < len; i ++){

//保存当前位置的值

int insertValue = arr[i];

//找到当前位置的前一个位置

int insertIndex = i - 1;

//和前一个位置进行比较,只要比前一个小,就把前一个位置的值赋给当前位置

//一直循环下去,直到自己成为了第一个数或者找到了一个比自己还小的数

while (insertIndex >= 0 && insertValue < arr[insertIndex]){

arr[insertIndex + 1] = arr[insertIndex];

insertIndex--;

}

//把之前保留的数和赋给当前找到的位置

arr[insertIndex + 1] = insertValue;

}

}

2.希尔排序基本思路:先将整个待排序的记录分割成若干子序列,分别对这些子序列进行排序,待整个完整的序列基本有序时,再对全体记录进行依次直接插入排序

举个例子:比如对于数组{9,12,8,98,36,29,190,54}1)初始数组如下:14015b7ca472e340b6cc05791e150ecd.png

2)选择一个增量gap,通常是数组长度的一半,这里gap取4

对同一子序列进行排序,排序结果如下:f8bb3b818cb80bed86d598e15b0437d7.png

3)对上一轮排序的结果再次选择一个分量,gap取上一次gap的一半为2,排序结果如下:2a6a076c94de00cb86c66f73d01244fa.png

4)再次取上一次gap的一半,为1,此时排序的是整个数组,排序结果如下,a164bba00cd5656deef3762874f7d567.png

当gap为1时,整个数组已经排好序

代码如下:实现希尔排序有两种算法,一种是交换法一种是移位法,前者的效率没有后者的高public class ShellSort {

public static void change(int[] arr, int aIndex, int bIndex){

int temp = arr[aIndex];

arr[aIndex] = arr[bIndex];

arr[bIndex] = temp;

}

//交换法

public static void sort(int[] arr){

int temp = 0;

for (int gap = arr.length/2; gap > 0; gap /= 2){

for (int i = gap; i < arr.length; i++ ){

for (int j = i - gap; j >= 0;j -= gap){

if (arr[j] > arr[j + gap]){

temp = arr[j];

arr[j] = arr[j + gap];

arr[j + gap] = temp;

}

}

}

}

}

//移位法

public static void sort2(int[] arr){

for (int gap = arr.length/2; gap > 0; gap /= 2){

//从第gap个元素,逐个对其所在的组进行直接插入排序

for (int i = gap; i < arr.length; i++){

int j = i;

int temp = arr[j];

if(arr[j] < arr[j-gap]){

while (j - gap >= 0 && temp < arr[j-gap]){

arr[j] = arr[j - gap];

j -=gap;

}

arr[j] = temp;

}

}

}

}

public static void main(String[] args) {

int[] arr = {7,5,9,3,12,8,20,18};

sort2(arr);

System.out.println(Arrays.toString(arr));

}

}

选择排序

1.简单选择排序基本思路:从数组中找到一个最大数,和数组的最后一个数交换(也可以最小),然后再从第一个开始到倒数第二个找到最大数和倒数第二个数交换,重复这样做,直到只剩一个第一个数,这样整个数组的序就排好了

举个例子,数组{7,5,9,3,2,1,12,20},总共需要7趟(数组的长度 - 1)

第一趟:最大数为20,和第7个数交换,7,5,9,3,2,1,12,20

第二趟:最大数为12,和第6个数交换,7,5,9,3,2,1,12,20

第三趟:最大数为9,和第5个数交换,7,5,1,3,2,9,12,20

第四趟:最大数为7,和第4个数交换,2,5,1,3,7,9,12,20

第五趟:最大数为5,和第3个数交换,2,3,1,5,7,9,12,20

第六趟:最大数为3,和第2个数交换,2,1,3,5,7,9,12,20

第七趟:最大数为2,和第1个数交换,1,2,3,5,7,9,12,20代码如下:public static int[] selectSort(int[] arr){

if (arr == null || arr.length <= 0 ) return null;

int len = arr.length;

for (int i = 0; i < len - 1; i ++){

int maxIndex = 0;

for (int j = 0; j < len - i; j ++){

if (compare(arr[j], arr[maxIndex])){

maxIndex = j;

}

}

change(arr, maxIndex, len - 1 - i);

}

return arr;

}

private static boolean compare(int a, int b){

if (a > b)

return true;

return false;

}

private static void change(int[] arr, int a, int b){

int temp = arr[a];

arr[a] = arr[b];

arr[b] = temp;

}

2.堆排序前期知识要了解堆排序,必须知道堆这种数据结构,堆是一个近似完全二叉树,这种数据结构有个特性,即子节点的键值或索引总是小于(大于)它的父节点,而堆又分为大项堆和小项堆

1)大项堆:每个节点的值都大于或等于其子节点的值

2)小项堆:每个节点的值都小于或等于其子节点的值

比如:

66b9ce9041a2621da4f1b9441a233be0.png860f2cd6cfa0c28e25ab6f0c58175c71.png基本思路1)把待排序的数组构建成一个大根堆,此时最大值就是根元素

2)把堆首元素和堆尾元素进行互换,此时最大值就在堆尾

3)把堆的第一个元素到堆长度-1个元素进行第一和第二步操作,直到堆的长度为1

举个例子,数组{1,5,9,10,19,8}1)首先找到第一个元素,发现它没有父节点,则不交换88532201ef091d4ec896b4b1db431078.png

2)找到第二个元素5,发现它比父节点的值1还要大,两个元素进行交换4f2bd1dbea0b3e0af0606669dafb5219.png

3)找到第三个元素9,发现它比父节点的值5还要大,两个元素进行交换e062beae6ce1b2b51fc24d8f16a02b15.png

4)找到第四个元素10,发现它比父节点的值1还要大,两个元素进行交换,交换之后发现它还是比父节点的值9大,两个元素再进行交换537b0eba16bb8e498757914edfa70ad4.png

5)找到第五个元素19,发现它比父节点的值9大,两个元素进行交换,交换之后发现它还是比父节点的值10大,两个元素再进行交换7578ce6904c75e5170868456a09a05c8.png

6)找到第六个元素8,发现它比父节点的值5大,两个元素进行交换4454e2ac0243541f92d9aff41694a822.png

7)上述6步骤把整个数组构建成了大项堆,之后把堆首元素19和堆尾元素5进行交换3e73019fd0ca99f1c62d9ee6d79d2e15.png

8)之后再把数组的length - 1个元素构建成大项堆,再次交换,直到要构建的数组的长度只剩一个

代码如下public class HeapSort{

public static void swap(int[] arr, int i, int j){

int temp = arr[i];

arr[i] = arr[j];

arr[j] = temp;

}

public static void main(String[] arg){

int[] arr = {1,5,2,4,9,10};

heapSort(arr);

System.out.println(Arrays.toString(arr));

}

public static void heapSort(int[] arr){

//构造大根堆

int size = arr.length;

System.out.println("1)构造大根堆:");

heapInsert(arr,0 , size);

int i = 1;

while (size > 1){

//固定最大值

swap(arr, 0 , size - 1);

System.out.print("t第" + i + "次固定最大值:");

System.out.println(Arrays.toString(arr));

size --;

//构造大根堆

System.out.println((++ i)+")构造大根堆:");

heapify(arr, 0 , size);

}

}

//构造大根堆(通过新插入的数上升)

public static void heapInsert(int[] arr, int start, int length){

for (int i = start; i < length; i ++){

//当前插入的索引

int currentIndex = i;

//父节点索引

int fatherIndex = (currentIndex - 1)/2;

while (arr[currentIndex] > arr[fatherIndex]){

//交换当前节点与父节点的位置

swap(arr, currentIndex, fatherIndex);

//重新计算当前节点与父节点的索引

currentIndex = fatherIndex;

fatherIndex = (currentIndex - 1) / 2;

}

System.out.println("t第" + i + "次交换:" + Arrays.toString(arr));

}

}

//将剩余的数构造储层大根堆

public static void heapify(int[] arr, int index, int size){

int left = 2 * index + 1;

int right = 2 * index + 2;

while (left < size){

int largestIndex = index;

//判断孩子中较大的值的索引(确保右孩子在size范围之内)

if (arr[left] < arr[right] && right < size){

largestIndex = right;

}else {

largestIndex = left;

}

//比较父节点的值与孩子中较大的值,并确定最大值的索引

if (arr[index] > arr[largestIndex]){

largestIndex = index;

}

//如果父节点索引是最大值的索引,那已经是大根堆了,则推出循环

if (index == largestIndex){

break;

}

//父节点不是最大值,与孩子中较大的值交换

swap(arr, largestIndex, index);

System.out.println("tleft:" + left + "tright:" + right);

System.out.println("tindex:" + index);

System.out.println("tlargestIndex:" + largestIndex);

System.out.println("t交换:" + Arrays.toString(arr));

//将索引指向孩子中较大的值的索引

index = largestIndex;

//重新计算交换之后的孩子的索引

left = 2 * index + 1;

right = 2 * index + 2;

}

}

}

交换排序

1.冒泡排序基本思路:排序从前向后,依次比较相邻元素的值,如果前一个元素比后一个元素小,则两个元素互换位置。

举个例子:如下数据{3,44,38,5,47,15,36,26,27,2,46,4,19,50,48}

用最基础的方法进行比较:

第一趟:3,38,5,44,15,36,26,27,2,46,4,19,47,48,50

第二趟:3,5,38,15,36,26,27,2,44,4,19,46,47,48,50

第三趟:3,5,15,36,26,27,2,38,4,19,44,46,47,48,50

第四趟:3,5,15,26,27,2,36,4,19,38,44,46,47,48,50

第五趟:3,5,15,26,2,27,4,19,36,38,44,46,47,48,50

第六趟:3,5,15,2,26,4,19,27,36,38,44,46,47,48,50

第七趟:3,5,2,15,4,19,26,27,36,38,44,46,47,48,50

第八趟:3,2,5,4,15,19,26,27,36,38,44,46,47,48,50

第九趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十一趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十二趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十三趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十四趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50

第十五趟:2,3,4,5,15,19,26,27,36,38,44,46,47,48,50代码实现:

一般来说,有几个数我们就要循环几趟,但是有的时候其实可能整个数组的顺序已经排序好了,但是可能还剩好几趟,比如上面的例子中,在第十趟的时候其实已经排号序了。所以在代码实现中,设置一个标志,如果一趟下来都没有两个相邻数之间进行交换,则表示整个数组已经排好序了,所以直接跳出循环。public class Bubble {

//比较两个数的大小

public static boolean compare(int a, int b){

if (a > b){

return true;

}

return false;

}

//交换两个数的位置

public static void change(int[] arr, int aIndex, int bIndex){

int temp = arr[aIndex];

arr[aIndex] = arr[bIndex];

arr[bIndex] = temp;

}

public static void sort(int[] arr){

boolean flag = false;//标志位

for (int i = 0; i < arr.length - 1; i++){

for (int j = 0; j < arr.length - i -1;j++){

if (compare(arr[j], arr[j + 1])){

flag = true;

change(arr, j, j+1);

}

}

if (!flag){

break;

}else {

flag = false;

}

}

}

}

2.快速排序基本思路:首先找到一个基准数,比这个数大的放到它的左边,比这个数小的放到它的右边,然后再分别对它的左右两边进行相同的操作,直到左右两边的数都排好序。

举个例子:数组arr={12,18,6,9,14,8,4,3,19},我们把数组的每一位数想象成一个房间,每个房间里装着一个人

令i = 0,j = 8,x=arr[i]; i代表最左边的下标,j代表最右边的下标,x为基准数

首先我们需要对arr进行第一次排序,让x左边全都是比它小的数,x的右边全都是比它大的数,我们从下标j开始往前查找,我们先把x从房间里请出来,此时arr[0]的房间是空的(但实际上这个房间还是有人的,这样是为了方便理解)

从后往前找,直到找到比x小的数:我们发现arr[8]并不大于x,那么令j--,这时我们发现arr[7]=3是小于x的,那么此时我们把3赋给arr[0],这就相当于把房间7的人放到了房间0里面,这时房间7空了,后面的房间空了就需要前面的房间来补此时数组为arr={3,18,6,9,14,8,4,3,19},i=0,j=7

从前往后找,直到找到比x大的数:此时arr[0]并不大于x,令i++,发现arr[1]=18大于12,那我们就可以把房间1的人放到空房间7此时数组为arr={3,18,6,9,14,8,4,18,19},i=1,j=7

现在是房间1空了,从后往前找,发现arr[6]=4小于x,把房间6的人放到了房间1此时数组为arr={3,4,6,9,14,8,4,18,19},i=1,j=6

现在是房间6空了,从前往后找,发现arr[4]=14大于x,把房间4的人放到了房间6此时数组为arr={3,4,6,9,14,8,14,18,19},i=4,j=6

现在是房间4空了,从后往前找,发现arr[5]小于x,把房间5的人放到了房间6此时数组为arr={3,4,6,9,8,8,14,18,19},i=4,j=5

现在是房间5空了,从前往后找,发现arr[5]不大于x,并且i和j相等了,那么第一次排序就结束了,站在门外的x进去了房间5此时数组为arr={3,4,6,9,8,12,14,18,19}

现在就把12左边的数再进行一次排序,12右边的数再进行一次排序,重复进行直到整个数组排序排好了

代码如下:public static void quickSort(int[] arr, int start, int end){

//边界条件

if (arr == null || arr.length <= 1 || start >= end) return;

//设置基准值x,左边索引i,右边索引j

int i = start,j = end;

int x = arr[i];

//只要i大于等于j,就一直找

while (i < j){

while (i < j && arr[j] >= x) j --;

if (i < j)

arr[i] = arr[j];

while (i < j && arr[i] < x) i ++;

if (i < j)

arr[j] = arr[i];

}

arr[i] = x;

//左右两边递归排序

quickSort(arr, start, i - 1);

quickSort(arr, i + 1, end);

}

归并排序基本思路:归并排序是采用分而治之的思想,将待排序的数组分成若干个数组,对每个数组进行排序,然后再将排序好的数组合并,如下图所示16fca79bd20b73be79d5ee147c11f7b6.png

举个例子:数组{1,4,6,7,9,2,3,5}结合上图,首先对整个数组进行递归等分,直到被分解出来的数组的长度为1,然后对相邻的两个数组进行有序的合并,直到所有分散的数组合并成一个有序数组

合并的步骤:比如上图中的数组{1,4,6,7}和数组{2,3,5,9}的合并,首先右边数组的第一个元素和左边数组的第一个元素进行比较,如果右边的小则把该元素放到一个临时的数组temp里存着,反过来也是一样。直到左右两边有一边被处理完,然后把剩余的另一边的数组填充到temp数组里,这样就合并完成

代码如下:public class Test {

public static void main(String[] args) {

int arr[] = { 8, 4, 5, 7, 1, 3, 6, 2 };

mergeSort(arr, 0, arr.length - 1, new int[arr.length]);

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; //t指向temp数组的当前索引

//1)首先,把左右两边的数据填充到temp数组中

//直到左右两边的有序序列有一边已经处理完毕

while (i <= mid && j <= right){

//如果左边的有序序列的当前元素小于等于右边的,则填充到temp数组,反之亦然

//填充完之后索引值递增

if (arr[i] <= arr[j]){

temp[t] = arr[i ++];

}else {

temp[t] = arr[j ++];

}

t ++;

}

//2)把剩余一边的有序序列填充到temp数组中

while (i <= mid){//如果左边序列还有剩余,则处理

temp[t ++] = arr[i ++];

}

while (j <= right){//如果右边序列还有剩余,则处理

temp[t ++] = arr[j ++];

}

//3)把temp数组的元素拷贝到arr数组

//并不是每次temp数组的总元素都和arr数组相等

t = 0; //初始化temp的起始索引

int tempLeft = left; //获取左边序列的起始索引

while (tempLeft <= right){

arr[tempLeft ++] = temp[t ++];

}

}

}

基数排序举例说明:比如待排序数组为{53, 42, 9, 4, 128, 64, 989}1)首先,获取所有元素的个位数,根据个位数放入到对应的桶中(桶有10个),然后按序将放入桶中元素拿出赋给原来的数组,则第一趟的结果为:42,53,4,64,128,9,9893ed7a1f798fec103e0ba5f2229628798.png

2)然后获取所有元素的十位数,根据十位数放入到对应的桶中,如果位数不够的自动补0,放入到0号桶中,则第二趟的结果为:4,9,128,42,53,64,98920e2d82b42a4f3d4fb731025252c1d58.png

3)最后获取所有元素的百位数,根据百位数放入到对应的桶中,如果位数不够的话也是自动补0,则第三趟的结果为:4,9,42,53,64,128,989e54a62b03a734bf457b0421056e0b8b2.png

注意:趟数和该元素的最大值的位数一致,如果该数组有千位的话还需要进行第四趟,根据千位数进行排序

代码如下:public class RadixSort {

public static void main(String[] args) {

int arr[] = { 53, 3, 542, 748, 14, 214};

radixSort(arr);

}

public static void radixSort(int[] arr){

//1、获取arr数组中的最大值的位数

int max = arr[0];

for (int temp : arr){

if (temp > max){

max = temp;

}

}

int maxLength = (max + "").length();

//2、初始化桶、以及每个桶对应的索引

//每个桶的长度为arr的长度(取最坏的情况)

int[][] bucket = new int[10][arr.length];

//记录每个桶实际放了多少数据

int[] bucketEleCounts = new int[10];

//3、循环处理桶

for (int i = 0, n = 1; i < maxLength; i ++, n *= 10){

//遍历数组,对每个数组的对应位进行排序处理(从个位数开始)

for (int j = 0; j < arr.length; j ++){

//获取该元素的对应位的值

int digit = arr[j] / n % 10;

//通过digit,将该元素放入相对应的桶中

bucket[digit][bucketEleCounts[digit] ++] = arr[j];

}

//按照桶的顺组,将每个桶中的数据放入到原来的arr数组中

int index = 0; //记录arr数组当前位置

//遍历每一个桶

for (int k = 0; k < bucketEleCounts.length; k ++){

//如果桶中有数据我们才放入arr中

int count = bucketEleCounts[k];

if (count != 0){

//循环第k个桶,将数据放入arr中

for (int l = 0; l < count; l ++){

arr[index ++] = bucket[k][l];

}

//每一轮处理过后,需要将bucketEleCount清零,为下一轮做准备

bucketEleCounts[k] = 0;

}

}

System.out.println("第" + (i + 1) + "轮,arr = " + Arrays.toString(arr));

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值