几种常见的内排序-java代码实现

84 阅读5分钟

概述

由于面试中,排序基本上必问知识,故实现常见排序算法代码(一点都不了解排序算法的跳过,毫无价值)

特别说明

  • 如果面试过程中让手写一个排序算法,推荐快速排序,又简单又装逼
  • 大多数性能都能保证的归并排序
  • 这些算法都是我随笔写的,只保证上述main方法正确,不保证算法一定正确,且不做边界校验

在这里插入图片描述

插入排序

直接插入排序

原理

  1. 国外直接插入排序演示,点击按钮Insertion Sort
  2. 国内直接插入排序演示`,推荐

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4};
		System.out.println(Arrays.toString(x));
		insertionSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void insertionSort(int[] nums) {
		int len = nums.length;
		int j;
		for (int i = 1; i < len; i++) {
			if (nums[i - 1] < nums[i]) {
				continue;
			}
			int num = nums[i];
			for (j = i - 1; j >= 0 && nums[j] > num; j--) {
				nums[j + 1] = nums[j];
			}
			nums[j + 1] = num;
		}
	}

优化直接插入排序,采用折半插入排序,如1/3/8已经是有序序列,减少比较次数
在这里插入图片描述

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		insertionSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void insertionSort(int[] nums) {
		int len = nums.length;
		int j;
		for (int i = 1; i < len; i++) {
			if (nums[i - 1] < nums[i]) {
				continue;
			}
			int num = nums[i];
			int low = 1;
			int high = i;
			while (low <= high) {
				int middle = (low + high) / 2;
				if (num < nums[middle - 1]) {
					high = middle - 1;
				} else {
					low = middle + 1;
				}
			}
			
			// position high
			for (j = i; j > high; j--) {
				nums[j] = nums[j - 1];
			}
			nums[high] = num;
		}
	}

2路插入排序

不常见,但是比直接插入排序、折半插入排序更快。参考:data.biancheng.net/view/67.htm…

希尔排序

原理参考:www.cnblogs.com/chengxiao/p…

下面我说一下我自己的理解,排序无非就是比较和交换,要优化效率,要么减少比较的次数,要么减少元素交换的次数,希尔排序是后者,减少元素交换的次数。
如何做得的呢,如图,普通的插入排序两个元素的间隔距离都为1,那就要想办法把间隔距离拉大,让原始一次性尽可能的移动更多的距离,这就是希尔排序。重要说明如下

  • 希尔排序最后一次的元素间隔一定是1,即直接插入排序
  • 希尔排序间隔都是从大到小,比如,5、2、1,如下图所示
    在这里插入图片描述
    代码实现
	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		shellSort(x, 5);
		shellSort(x, 3);
		shellSort(x, 1);
		System.out.println(Arrays.toString(x));
	}
	
	/**
	 * var dk is incremental
	 */
	public static void shellSort(int[] nums, int dk) {
		int len = nums.length;
		int j;
		for (int i = dk; i < len; i++) {
			if (nums[i - dk] < nums[i]) {
				continue;
			}
			int num = nums[i];
			for (j = i - dk; j >= 0 && nums[j] > num; j -= dk) {
				nums[j + dk] = nums[j];
			}
			nums[j + dk] = num;
		}
	}

与直接插入排序代码差异比较,其实就是多了一个增量dk,由1变成任意大于1的整数
在这里插入图片描述

交换排序

冒泡排序

原理

  1. 国外 冒泡排序演示,点击按钮Bubble Sort
  2. 国内冒泡排序序演示`,推荐

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		bubbleSort(x);
		System.out.println(Arrays.toString(x));
	}
	

	public static void bubbleSort(int[] nums) {
		int len = nums.length;
		for (int i = 0; i < len - 1; i++) {
			for (int j = 0; j < len - 1 - i; j++) {
				if (nums[j] > nums[j+1]) {
					int temp = nums[j];
					nums[j] = nums[j+1];
					nums[j+1] = temp;
				}
			}
		}
	}

快速排序

原理

  1. 国外直接插入排序演示,点击按钮Quick Sort

个人理解,快速排序属于二路比较,如果是一路扫描,需要引入与数据等大的存储空间,如果是二路扫描,只需要额外的一个存储空间(个人理解,不喜勿喷)

在这里插入图片描述

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		quickSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void quickSort(int[] nums) {
		quickSort(nums, 0, nums.length - 1);
	}

	public static void quickSort(int[] nums, int low, int high) {
		if (low < high) {
			int location = partition(nums, low, high);
			// 分治
			quickSort(nums, low, location - 1);
			quickSort(nums, location + 1, high);
		}
	}

	// 
	private static int partition(int[] nums, int low, int high) {
		int flagNum = nums[low];
		while (low < high) {
			while (low < high && nums[high] >= flagNum) {
				high--;
			}
			nums[low] = nums[high];
			while (low < high && nums[low] <= flagNum) {
				low++;
			}
			nums[high] = nums[low];
		}
		nums[low] = flagNum;
		return low;
	}

选择排序

简单选择排序

原理

  1. 国外简单选择排序演示,点击按钮Selection Sort
  2. 国内简单选择排序演示`,推荐

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		selectionSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void selectionSort(int[] nums) {
		for (int i = 0; i < nums.length - 1; i++) {
			int minIndex = i;
			for (int j = i + 1; j < nums.length; j++) {
				if (nums[minIndex] > nums[j]) {
					minIndex = j;
				}
			}
			if (minIndex != i) {
				int temp = nums[minIndex];
				nums[minIndex] = nums[i];
				nums[i] = temp;
			}
		}
	}

堆排序

原理,理论参考完全二叉树概念

  1. 国外简单选择排序演示
  2. 国内堆排序演示

最小堆构建过程
在这里插入图片描述

代码实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		heapSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void heapSort(int[] nums) {
		// 构建堆
		for (int i = nums.length / 2; i >= 1 ; i--) {
			heapAdjust(nums, i, nums.length);
		}
		
		for (int i = nums.length; i > 1 ; i--) {
			// 堆中最大元素与第一个元素交换
			int temp = nums[0];
			nums[0] = nums[i - 1];
			nums[i - 1] = temp;
			// 第一个元素构造最大堆,剔除最后一个元素
			heapAdjust(nums, 1, i - 1);
		}
	}
	
	// 大顶堆调整,注:数组下标0开始,所有取值都-1
	public static void heapAdjust(int[] nums, int start, int length) {
		int top = nums[start - 1];

		for (int i = start * 2; i <= length; i = i * 2) {
			// 1. i < m必有两个孩子,2start、2start+1
			// 2. 选择孩子中较大的孩子
			if (i < length && nums[i - 1] < nums[i]) {
				i++;
			}
			// 3. 较大的孩子与当前的父节点比较,如果父节点已经是最大的,则完成
			if (top > nums[i - 1]) {
				break;
			}
			// 4. 交换值后,递归继续调整
			nums[start - 1] = nums[i - 1];
			start = i;
		}
		// 5. 
		nums[start - 1] = top;
	}

归并排序

原理

  1. 国外直接插入排序演示,点击按钮Merge Sort,推荐
  2. 国内直接插入排序演示`

在这里插入图片描述

2-路归并排序实现

	public static void main(String[] args) {
		int[] x = new int[] {5, 1, 3, 7, 11, 4, 6, 2, 0, 13, -3};
		System.out.println(Arrays.toString(x));
		mergeSort(x);
		System.out.println(Arrays.toString(x));
	}
	
	public static void mergeSort(int[] nums) {
		// 避免生成大量的临时对象,声明等大数组存储空间
		int[] tmp = new int[nums.length];
		doSort(nums, tmp, 1, nums.length);
	}

	// 注:数组下标0开始,所有取值都-1
	public static void doSort(int[] nums, int[] tmp, int begin, int end) {
		if (begin == end) {
			tmp[begin - 1] = nums[begin - 1];
		} else {
			int middle = (begin + end) / 2;
			doSort(nums, tmp, begin, middle);
			doSort(nums, tmp, middle + 1, end);
			doMerge(tmp, nums, begin, middle, end);
			System.arraycopy(nums, begin - 1, tmp, begin - 1, (end - begin) + 1);
		}
	}

	// 注:数组下标0开始,所有取值都-1
	public static void doMerge(int[] from, int[] to, int begin, int middle, int end) {
		int index = begin;
		int position = middle + 1;
		while (begin <= middle && position <= end) {
			if (from[begin - 1] < from[position - 1]) {
				to[index - 1] = from[begin - 1];
				begin++;
			} else {
				to[index - 1] = from[position - 1];
				position++;
			}
			index++;
		}
		while (begin <= middle) {
			to[index - 1] = from[begin - 1];
			begin++;
			index++;
		}
		while (position <= end) {
			to[index - 1] = from[position - 1];
			position++;
			index++;
		}
	}