排序算法源码示例
代码将常见的排序算法源码放在一个类中。本文算法已经经过验证,可以直接拿来进行使用。源码中主要包含插入排序(直接插入排序,二分插入排序,希尔排序),交换排序(冒泡排序,快速排序(由quick,partition两个方法完成)),选择排序(简单选择排序,堆排序(由heap,buildMaxHeap,adjustDown三个方法完成)),归并排序(举例了二路归并排序的例子,由mergefrom2和merge两个方法完成)
public class Sort{
//插入排序
class InsertSort{
/**
* 插入排序
* 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。
* 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)
* 时间复杂度 O(n^2) 稳定 有相同元素则往后插入,不会改变相对位置
*
* @param src
*/
public void direct(int[] src){
for (int i = 1; i < src.length; i++) {
//i 表示处理第几个元素 内层循环j用来前半段的有序插入 先找到插入的位置,然后将元素全部后移
int insertIndex = i;
for (int j = 0; j < i; j++) {
if (src[i] < src[j]) {
insertIndex = j;
//找到下标后立即退出内层循环
break;
}
}
//后移所有元素之前,先暂存当前需要插入的位置的元素,等插入结束后再补齐改元素
int temp = src[i];
for (int k = i; k > insertIndex; k--) {
src[k] = src[k - 1];
}
src[insertIndex] = temp;
}
}
//二分插入排序
public void half(int[] src){
int length = src.length;
for(int i=1; i<length; i++){
int temp = src[i];
int left = 0;
int right = i-1;
while(left<=right){
int mid = (left+right)/2;
if(temp < src[mid])
right = mid - 1;
else
left = mid + 1;
}
for(int j=i; j>left; j--)
src[j] = src[j-1];
src[left] = temp;
}
}
/**
* 希尔排序
* 是先将数组分为 src/d个组,对每个组进行直接插入排序
* 第二轮 d = d/2 在进行直接插入排序 ,一直这样进行下去
* 最后一轮的 d=1 即全组的直接插入排序
*
* @param src
*/
public void shell(int[] src){
int groupNum = src.length >> 2;
while (groupNum > 0) {
for (int i = groupNum; i < src.length; i++) {
int insertIndex = i;
for (int j = 0; j < i; j++) {
if (src[i] < src[j]) {
insertIndex = j;
break;
}
}
int temp = src[i];
for (int k = i; k > insertIndex; k--) {
src[k] = src[k - 1];
}
src[insertIndex] = temp;
}
groupNum >>= 2;
}
}
}
//交换排序
class SwapSort{
/**
* 冒泡排序
* 两两比较相邻的元素,将最大的放在后面
* 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
* O(n^2) 稳定的 因为 src[j] < src[j - 1] 没加等号,所以想等的元素相对位置不会发生改变
* @param src
*/
public void bubble(int[] src){
int length = src.length-1;
for(int i=0; i<length; i++){
boolean flag = false;
for(int j=length; j>i; j--)
if(src[j] < src[j-1]){
int temp = src[j];
src[j] = src[j-1];
src[j-1] = temp;
flag = true;
}
if(!flag)
return ;
}
}
/**
* 快速排序
* 基本思想是基于分治的,在待排序的序列中选取一个元素作为基准,通过一趟排序将待排序表划分为独立的两部分,
* 部分都小于该元素,一部分都大于该元素。而后分别递归两个子序列,直到每个部分都只有一个元素或为空为止。
* O(n*log(n)) 不稳定
*
* @param src
*/
public void quick(int[] src, int left, int right){
if(left < right){
int pivot = partition(src, left, right);
quick(src, left, pivot-1);
quick(src, pivot+1, right);
}
}
/**
* 返回基准值的最终下标,并且把基准值放在最终位置
*
* @param src
* @param low
* @param high
* @return
*/
public int partition(int[] src, int left, int right){
int pivot = src[left];
while(left < right){
while(left < right && src[right] >= pivot)
--right;
src[left] = src[right];
while(left < right && src[left] <= pivot)
++left;
src[right] = src[left];
}
src[left] = pivot;
return left;
}
}
//选择排序
class SelectSort{
/**
* 选择排序
* 首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置
* 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
* 重复第二步,直到所有元素均排序完毕。
* 时间复杂度 O(n^2) 不稳定. 举个例子,序列5 8 5 2 9, 我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
*
* @param src
*/
public static void sort(int[] src) {
for (int i = 0; i < src.length - 1; i++) {
int minIndex = i;
for (int j = i + 1; j < src.length; j++) {
minIndex = src[j] < src[minIndex] ? j : minIndex;
}
int temp = src[i];
src[i] = src[minIndex];
src[minIndex] = temp;
}
}
/**
* 堆排序
* 看成是完全二叉树序列,利用完全二叉树和孩子节点的内在关系,在当前的无序区中选择关键字最小的,
* 输出,然后将最后一个元素和堆顶元素进行交换,破坏了堆结构,再进行调整,直至最后一个(完成排序)
* 步骤 1.构造初始堆 2.调整堆结构 3.输出根节点 4.将最后一个叶子节点和堆根元素互换位置 5.调整堆结构
* 构建大根堆(升序). 小根堆(降序)
* 时间复杂度 O(n*log(n)) 不稳定
*
* @param src
*/
public void heap(int[] src){
//构建的是大根堆
buildMaxHeap(src);
// 通过这个数组调整,最大的元素放到最后了
for (int i = src.length - 1; i > 0; i--) {
int temp = src[i];
src[i] = src[0];
src[0] = temp;
adjustDown(src, 0, i - 1);
}
}
/**
* 建立初始的大根堆
*
* @param src
*/
public void buildMaxHeap(int[] src){
for(int i=(src.length-1)/2; i>=0; i--)
adjustDown(src, i, src.length-1);
}
/**
* 调整堆元素 调整 以i为根节点,到j结束的堆为 大根堆
*
* @param src
* @param i
* @param j
*/
private static void adjustDown(int[] src, int i, int j) {
int temp = src[i];
for (int k = 2 * i + 1; k <= j; k = k*2 +1) {
//将k的值变为左右儿子中较大的那个的下标 k为左儿子 k+1 为右儿子
if (k < j && src[k] < src[k + 1]) {
k++;
}
//如果父亲比儿子中较大的那个还大,则不需要交换了
if (temp > src[k]) {
break;
} else {
src[i] = src[k];
i = k;
}
}
src[i] = temp;
}
}
//归并排序
class MergeSort{
/**
* 归并排序
* 二路并归排序
* 将两个有序的表组合成一个新的有序表。假定待排序表含有n个记录,则可看成是n个有序表,每个表的长度为1,
* 然后两两归并,直到合并成一个长度为n的有序表为止.
* 时间复杂度 O(n*log(n)) 稳定
*
* @param src
*/
public void mergefrom2(int[] src, int left, int right){
if(left < right){
int mid = (left+right) / 2;
mergefrom2(src, left, mid);
mergefrom2(src, mid+1, right);
merge(src, left, mid, right);
}
}
public void merge(int[] src, int left, int mid, int right){
int[] src0 = new int[src.length];
System.arraycopy(src, 0, src0, 0, src.length);
int i,j,k;
for(i=left, j=mid+1, k=i; i<=mid && j<=right; k++)
src[k] = src0[i] <= src0[j] ? src0[i++] : src0[j++];
while(i<=mid) src[k++] = src0[i++];
while(j<=right) src[k++] = src0[j++];
}
}
}