Java常用排序算法/程序员必须掌握的8大排序算法

本文由网络资料整理而来,如有问题,欢迎指正!

  

参考链接:维基百科-排序算法

// 排序原始数据

// 排序原始数据
private static final int[] NUMBERS =
{49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64, 5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15, 35, 25, 53, 51};

 

插入排序 是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用in-place排序(即只需用到的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。

基本思路: 取第 N 个元素( N 必须 >1)【假设 N 前面的元素,都已排好序了,一般从第2个元素开始比较】。
用 N 跟(N的下标-1)的元素比较,满足条件的,将(N的下标-1)元素向后移动一位。然后再将N 跟(N的下标-2)元素比较,满足条件的,将(N的下标-2)元素向后移动一位,以此类推,直到 不满足的时候,就停止比较。这里,假设比较(N的下标-5)元素的时候,不满足了。然后直接将 N 赋值给 (N的下标-4)的元素。

示例图:


JAVA代码:

 public static void insertSort(int[] array) {
     for (int i = 1; i < array.length; i++) {
         int temp = array[i];//从下标为 1 的元素,即第二位数开始 比较
        
         int j = i - 1;// 前一位下标,
        
         for (; j >= 0 && array[j] > temp; j--) {

             //将大于temp的值整体后移一个单位,
                array[j + 1] = array[j];

             //j-- 表示,将下一个比较对象向前移一个单位
             
         }
        //直到不满足条件,跳出内循环。
        //此时的 j的值,就是那个不满足条件的比较对象的下标
        //然后,将temp 插在它后面。
         array[j + 1] = temp;
        
        //最后 i++,继续下一轮外循环
     }
     System.out.println(Arrays.toString(array) + " insertSort");
 }

 

希尔排序  也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

  • 插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
  • 但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位

基本思路: 基于上面说的两点性质,希尔排序,是将数据,从只能移动一位,改变成移动 N 位, 最后 N变成1,做最后一次 插入排序,然后 变成0,结束。
通俗的讲:就是在 做插入排序前,先做几次 粗略的 排序,以减少 插入排序的 移动次数。
因为 插入排序最大的弊端,就是每次只能移动一位,假设,要排序的数组是一段非常无序的,并且 每次循环都需要移动元素,那这就相当耗时,所以希尔排序的目的,是在执行最后的插入排序前,先用递增量,来对数组做几次粗略的排序,最后在将这个递增量变成1,已达到最后以插入排序做最后的排序,这样,就可以大大减少插入排序的循环次数和移动次数。

JAVA代码:

public static void shellSort(int[] array,int n) {
    // array 是将要排序的数组
   	// n 为递增量 

		int i;
		int j;
		int temp; 

		int gap = 1;//默认值是1(如果是1,那就是 普通的 插入排序了),
		int len = array.length;//数组长度
		
        // len/n= 取整数倍 (13/5=2。 13%5=3)
		while (gap < len / n) { 
			gap = gap * n + 1;//取 n 的整数倍 +1
                              //(+1,是为了保证,最后是以1为递增量,也就是插入排序)
		}
		//gap 为实际的运算递增量,随循环而变小,直至变为0,结束
        /*
           在插入排序中,递增量是1,效率低 , 
           而希尔排序,就是通过增加递增量,
           来提高 排序的效率
        */
		for (; gap > 0; gap /= n) {
			for (i = gap; i < len; i++) {
				temp = array[i];
				for (j = i - gap; j >= 0 && array[j] > temp; j -= gap) {
					array[j + gap] = array[j];
				}
				array[j + gap] = temp;
			}
		}
		System.out.println(Arrays.toString(array) + " shellSort");
		
}

简单选择排序  是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

选择排序的主要优点与数据移动有关。如果某个元素位于正确的最终位置上,则它不会被移动。选择排序每次交换一对元素,它们当中至少有一个将被移到其最终位置上,因此对个元素的表进行排序总共进行至多{\displaystyle n-1}次交换。在所有的完全依靠交换去移动元素的排序方法中,选择排序属于非常好的一种。
通俗讲:就是取数组第一位,依次跟后面所有的元素进行比较,一次完整的内循环后,满足条件的,交换位置。然后接着比第二位,一次类推,直到最后一位。
实例:

public static void selectSort(int[] array) {
		int position = 0;// 满足条件的数据 所在位置
		for (int i = 0; i < array.length; i++) {
			int j = i + 1;
			position = i;
			int temp = array[i];// 被比较的数,从第i位开始,依次向后对比。
			for (; j < array.length; j++) {
				if (array[j] < temp) {
					temp = array[j];// 获取最小数
					position = j;//以及它的位置
				}
			}
			array[position] = array[i];
			array[i] = temp;
		}
		System.out.println(Arrays.toString(array) + " selectSort");
}

堆排序  是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子节点的键值或索引总是小于(或者大于)它的父节点。
基本思路:将要排序的数组 array,看着一颗完全二叉树结构。 array[0] 表示 根节点,array[length-1] 表示尾叶结点。
以数组索引,做二叉树的 编号。取 尾叶节点的 双亲节点, 即: 双亲的数组索引 = length/2-1 = (length-2)/2,依次向下做递归。
通俗讲:分为 三 步骤

  1. 先将要排序的数组堆化(转成 满足完全二叉树特性的数组,并以 最大(或最小,根据需求) 数做根节点
  2. 转完后,将根节点 和 最后的 尾结点交换,即(数组头尾元素交换
  3. 然后将最后的元素,踢出下一轮的堆化。所谓的踢出,就是将第二轮的堆化,的数组长度参数减一
    依次类推,直到最后两个元素交换

实例:




JAVA代码:

public static void heapSort(int[] array) {
    /*
     *  第一步:将数组堆化
     *  beginIndex = 第一个非叶子节点。
     *  从第一个非叶子节点开始即可。无需从最后一个叶子节点开始。
     *  叶子节点可以看作已符合堆要求的节点,根节点就是它自己且自己以下值为最大。
     */
    int len = array.length - 1;
    int beginIndex = (len - 1) >> 1;
    for (int i = beginIndex; i >= 0; i--) {
        maxHeapify(i, len, array);
    }
    /*
     * 第二步:对堆化数据排序
     * 每次都是移出最顶层的根节点A[0],与最尾部节点位置调换,同时遍历长度 - 1。
     * 然后从新整理被换到根节点的末尾元素,使其符合堆的特性。
     * 直至未排序的堆长度为 0。
     */
    for (int i = len; i > 0; i--) {
        swap(0, i, array);
        maxHeapify(0, i - 1, array);
    }
    System.out.println(Arrays.toString(array) + " heapSort");
}
private static void swap(int i, int j, int[] arr) {
    int temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}
/**
 * 调整索引为 index 处的数据,使其符合堆的特性。
 *
 * @param index 需要堆化处理的数据的索引
 * @param len   未排序的堆(数组)的长度
 */
private static void maxHeapify(int index, int len, int[] arr) {
    int li = (index << 1) + 1; // 左子节点索引
    int ri = li + 1;           // 右子节点索引
    int cMax = li;             // 子节点值最大索引,默认左子节点。
    if (li > len) {
        return;       // 左子节点索引超出计算范围,直接返回。
    }
    if (ri <= len && arr[ri] > arr[li]) // 先判断左右子节点,哪个较大。
    { cMax = ri; }
    if (arr[cMax] > arr[index]) {
        swap(cMax, index, arr);      // 如果父节点被子节点调换,
        maxHeapify(cMax, len, arr);  // 则需要继续判断换下后的父节点是否符合堆的特性。
    }
}

冒泡排序  是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
基本思路: 内循环,完成 1 轮,能把 满足条件的 最大或最小,移动到数组最后。然后 i++,内循环,的长度就会length -1-i (即,不在操作,上一轮被移动到最后的那个元素。)直至 结束。

public static void bubbleSort(int[] array) {
    int temp = 0;
    for (int i = 0; i < array.length - 1; i++) {
        for (int j = 0; j < array.length - 1 - i; j++) {
            if (array[j] > array[j + 1]) {
                temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
    System.out.println(Arrays.toString(array) + " bubbleSort");
}

快速排序  又称划分交换排序,简称快排,一种排序算法,最早由东尼·霍尔提出。在平均状况下,排序n个项目要{\displaystyle \ O(n\log n)}大O符号)次比较。在最坏状况下则需要{\displaystyle O(n^{2})}次比较,但这种状况并不常见。事实上,快速排序{\displaystyle \Theta (n\log n)}通常明显比其他算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地达成。
基本思路:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。
如何分成大小两部分的方法有很多,这里是将,基准元素 先后依次比较 数组尾首两端,向中间挤压。

通俗讲:就是找到元素,在数组中,本应该待的位置。 一般取第一或最后一个元素。 跟相反方向的元素,逐个比较(如果取的是第一个,就从数组尾巴开始向前比较,反之,则从数组开始向后比较)。当不满足条件,在跟其相反方向逐个比较,直到 被比较的元素的位置,不大于起始位置,不小于结束位置时,就是它的正确位置,并以此位置,将数组分成两部分,再以此相同的方法,找各个元素的正确位置。

package test.seowen;

import java.util.Arrays;

public class QuickSort {
	public static void main(String[] args) {
		int[] array = { 4, 3, 6, 2, 7, 1, 5, 4, 3, 6, 2, 7, 1, 54, 3, 6, 2, 7, 1, 5 };
		QuickSort.qsort(array);
		System.out.println(Arrays.toString(array) + " quickSort");
	}

	public static void qsort(int array[]) {
		if (array != null && array.length > 0) {
			_quickSort(array, 0, array.length - 1);
		}
	}

	private static void _quickSort(int array[], int begin, int lastIndex) {
		if (begin < lastIndex) {
			int middle = _getMiddleSort(array, begin, lastIndex);// 将list数组进行一分为二
			_quickSort(array, begin, middle - 1); // 对低字表进行递归排序
			_quickSort(array, middle + 1, lastIndex);// 对高字表进行递归排序
		}
	}

	private static int _getMiddleSort(int array[], int begin, int lastIndex) {
		int temp = array[begin];// 基准数
		while (begin < lastIndex) {
			// 如果 末尾元素,大于 基准数,则末尾指针,向前移动一位。
			while (begin < lastIndex && array[lastIndex] >= temp) {
				lastIndex--;
			}
			// 否则,把末尾的这个数,赋值给 首部元素。
			array[begin] = array[lastIndex];

			// 然后 从首部元素,向后比较
			while (begin < lastIndex && array[begin] <= temp) {
				begin++;
			}
			// 否则,把末尾的这个数,赋值给 首部元素。
			array[lastIndex] = array[begin];

		}

		array[begin] = temp;
		System.out.println(begin);
		return begin;
	}

}

归并排序  是创建在归并操作上的一种有效的排序算法,效率{\displaystyle O(n\log n)}大O符号)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。
基本思想:归并(Merge)排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

(2)实例:


JAVA 代码

public static void mergingSort(int[] array) {
    sort(array, 0, array.length - 1);
    System.out.println(Arrays.toString(array) + " mergingSort");
}
 
private static void sort(int[] data, int left, int right) {
    if (left < right) {
        //找出中间索引
        int center = (left + right) / 2;
        //对左边数组进行递归
        sort(data, left, center);
        //对右边数组进行递归
        sort(data, center + 1, right);
        //合并
        merge(data, left, center, right);
    }
}
 
private static void merge(int[] data, int left, int center, int right) {
    int[] tmpArr = new int[data.length];
    int mid = center + 1;
    //third记录中间数组的索引
    int third = left;
    int tmp = left;
    while (left <= center && mid <= right) {
        //从两个数组中取出最小的放入中间数组
        if (data[left] <= data[mid]) {
            tmpArr[third++] = data[left++];
        } else {
            tmpArr[third++] = data[mid++];
        }
    }
 
    //剩余部分依次放入中间数组
    while (mid <= right) {
        tmpArr[third++] = data[mid++];
    }
 
    while (left <= center) {
        tmpArr[third++] = data[left++];
    }
 
    //将中间数组中的内容复制回原数组
    while (tmp <= right) {
        data[tmp] = tmpArr[tmp++];
    }
}

基数排序  是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

public static void radixSort(int[] array) {
    //首先确定排序的趟数;
    int max = array[0];
    for (int i = 1; i < array.length; i++) {
        if (array[i] > max) {
            max = array[i];
        }
    }
    int time = 0;
    //判断位数;
    while (max > 0) {
        max /= 10;
        time++;
    }
 
 
    //建立10个队列;
    ArrayList<ArrayList<Integer>> queue = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
        ArrayList<Integer> queue1 = new ArrayList<>();
        queue.add(queue1);
    }
 
 
    //进行time次分配和收集;
    for (int i = 0; i < time; i++) {
        //分配数组元素;
        for (int anArray : array) {
            //得到数字的第time+1位数;
            int x = anArray % (int)Math.pow(10, i + 1) / (int)Math.pow(10, i);
            ArrayList<Integer> queue2 = queue.get(x);
            queue2.add(anArray);
            queue.set(x, queue2);
        }
        int count = 0;//元素计数器;
        //收集队列元素;
        for (int k = 0; k < 10; k++) {
            while (queue.get(k).size() > 0) {
                ArrayList<Integer> queue3 = queue.get(k);
                array[count] = queue3.get(0);
                queue3.remove(0);
                count++;
            }
        }
    }
    System.out.println(Arrays.toString(array) + " radixSort");
}


--------------------- 

原文:https://blog.csdn.net/qy1387/article/details/7752973

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值