千奇百怪的排序算法(Java 代码)

快速排序

分治的思想,选择一个基准点,将小于这个基准点的都放在基准点的左边,大于这个基准点的都放在这个基准点的右边

实现方式:

  • 1 挖坑填数
  • 2 左右指针

特别注意!(基准点的选择一定要是随机的)
我们每次递归使用的是区间第一个(或最后一个)元素作为基准点。

如果数据是正序或者逆序时,我们这样选取的基准值显然是不对的,这样的其他数据都在基准值的一侧,我们是无法划分出两个区域来分开排序的。

快速排序为什么快,就是因为基准点的两边同时排序。如果只能分出一遍来,那么这个排序算法就退化了。

以下三个图,说明了选取基准点的重要性(数据为一百万个有序元素):

  • 使用第一个元素作为基准点。栈溢出了
    在这里插入图片描述
  • 使用随机数
    在这里插入图片描述
  • 使用 三个点(l,r,mid)的中间值

代码:挖坑填数法实现

public class QuickSort {

    static int[] quick_sort(int[] nums, int l, int r){
        if (l + 1 >= r) {
            return null;
        }
        int first = l, last = r - 1, key = nums[first];
        while (first < last){
            while(first < last && nums[last] >= key) {
                --last;
            }
            nums[first] = nums[last];
            
            while (first < last && nums[first] <= key) {
                ++first;
            }
            nums[last] = nums[first];
        }
        nums[first] = key;

        quick_sort(nums, l, first);
        quick_sort(nums, first + 1, r);
        return nums;
    }
}

// 调用方法示例
QuickSort.quick_sort(nums, 0, nums.length)

补充:选取基准点 pivot 的最好方式:

	public static int getMid(int[] nums, int l, int r) {
		int mid = (l+r) >>> 1;
		// 将最大的元素交换到 r
		if(nums[mid] > nums[r]) {
			swap(nums, l, mid);
		}
		if(nums[l] > nums[r]) {
			swap(nums, l, r);
		}
		// 剩下的两个元素中,将第二大的放在放在 l位置
		if(nums[l] < nums[mid]) {
			// 如果l是小一点的,交换
			swap(nums, l, mid);
		}
		return l;
	}

注意!快速排序并不是一个稳定的算法

例如:[5, 4 ,9 , 1, 1, 1]
基准点选定4,那么最右边的 1,就会跑到4那里取,这样破坏了原本的相同的数字之间的相对排序。

稳定意味着程序的健壮性,比方说数据库中对某一列进行排序,如果是不稳定的排序。就会破坏此列相同数据的的其它列的顺序。

归并排序

public class MergeSort {

    static int[] merge_sort(int[] nums, int l, int r, int[] temp) {
        if (l + 1 >= r) {
            return null;
        }
        int m = l + (r - l) / 2;
        merge_sort(nums, l, m, temp);
        merge_sort(nums, m, r, temp);
        int p = l, q = m, i = l;
        while (p < m || q < r) {
            if (q >= r || (p < m && nums[p] <= nums[q])) {
                temp[i++] = nums[p++];
            } else {
                temp[i++] = nums[q++];
            }
        }
        for (i = l; i < r; ++i) {
            nums[i] = temp[i];
        }
        return nums;
    }

}

插入排序

  • 将数组分为两部分:[排序好了的][没有排序的]
  • 每次将右边的第一个,从右到左扫描排序好了的,如果发现小于就交换位置,否则不变
public class InsertionSort {
    static int[] insertion_sort(int[] nums, int n) {
        for (int i = 0; i < n; ++i) {
            for (int j = i; j > 0 && nums[j] < nums[j-1]; --j) {
                int temp = nums[j];
                nums[j] = nums[j-1];
                nums[j-1] = temp;
            }
        }
        return nums;
    }
}

冒泡排序

原理:比较两个相邻的元素,将值大的元素交换到右边

思路:依次比较相邻的两个数,将比较小的数放在前面,比较大的数放在后面。

这样一来:

  • 每一轮交换都会把最大的元素放在最后面,
  • 下一轮交换的时候,已经确定了的(右边的)元素就不用在进行交换了。(j<n-i+1的由来)
  • 如果此轮没有进行交换,说明数组已经有序,退出for循环
public class BubbleSort {
    static int[] bubble_sort(int[] nums, int n) {
        boolean swapped;
        for (int i = 1; i < n; ++i) {
            swapped = false;
            for (int j = 1; j < n - i + 1; ++j) {
                if (nums[j] < nums[j-1]) {
                    int temp = nums[j];
                    nums[j] = nums[j-1];
                    nums[j-1] = temp;
                    swapped = true;
                }
            }
            // 如果没有发生交换,说明已经有序
            if (!swapped) {
                break;
            }
        }
        return nums;
    }
}

选择排序

算法思想:
从头至尾扫描序列,找出最小的一个元素,和第一个元素交换。
接着从剩下的元素中继续这种方式,最终得到一个有序序列。

public class SelectionSort {
    static int[] selection_sort(int[] nums, int n) {
        int mid;
        for (int i = 0; i < n - 1; ++i) {
            mid = i;
            for (int j = i + 1; j < n; ++j) {
                if (nums[j] < nums[mid]) {
                    mid = j;
                }
            }
            int temp = nums[mid];
            nums[mid] = nums[i];
            nums[i] = temp;
        }
        return nums;
    }
}


调用以上排序代码

public class Main {
    public static void main(String[] args) {
        int[] nums = {1, 3, 5, 7, 2, 6, 4, 8, 9, 2, 8, 7, 6, 0, 3, 5, 9, 4, 1, 0};
        int[] temp = new int[nums.length];
        printLog(QuickSort.quick_sort(nums, 0, nums.length), "quickSort");
        printLog(MergeSort.merge_sort(nums, 0, nums.length, temp), "mergeSort");
        printLog(InsertionSort.insertion_sort(nums, nums.length), "insertionSort");
        printLog(BubbleSort.bubble_sort(nums, nums.length), "bubbleSort");
        printLog(SelectionSort.selection_sort(nums, nums.length), "selectionSort");
    }

    public static void printLog(int[] rets, String desc) {
        System.out.println(desc);
        for (int num : rets) {
            System.out.print(num + ", ");
        }
        System.out.println("\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值