常用排序方法(java实现)

/**本文假设数据用数组储存,排序元素为整数,升序排序**/

目录

一、选择排序

二、插入排序

三、冒泡排序

四、归并排序

五、快速排序

六、堆排序

七、桶排序

八、基数排序

九、外部排序


一、选择排序

思路:在数组中找到最小元素,并将其和第一个元素交换,直到数组中仅剩最后一个元素。

先假设第一个数为最小,则最小元素的下标为0,然后与后面的数挨个进行比较,当后面的数比当前最小数小时,更新最小下标。由此找到最小数,将它和第一个数交换。此时第一个数已经排好了。

然后再设第二个数最小,重复上述过程,前两个数就排好了。

循环直到数组中只剩最后一个数,则全部排序完毕。

时间复杂度O(n^2) 

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

二、插入排序

思路:从第一位开始重复将新的元素添加到一个已经排好序的数组中。

从 i 为1开始,

假设前 i 个数都已经排好,现在插入第 i + 1 个数,先将第 i + 1 个数放到临时变量currentE中,与前面的数比较,若小于前面的数,则让前面的数向后移动一位,直到currentE大于前面的数,则把currentE中的数放在这个数的后面。这样前 i + 1 个数就排好了。

时间复杂度O(n^2) 

	public static void insertionSort(int[] nums) {
		for(int i = 0; i < nums.length; i++) {
			int k;
			int currentE = nums[i];
			for(k = i - 1; k >= 0 && currentE < nums[k]; k--) {
				nums[k + 1] = nums[k];
			}
			nums[k + 1] = currentE;
		}
	}

三、冒泡排序

思路:多次遍历数组,每次遍历中连续比较相邻元素,如果元素没有按顺序排列,则互换位置,否则保持不变。

第一次遍历后,最后一个元素最大,第二次遍历后倒数第二个元素成为第二大,最多遍历n - 1 次排完所有数。若在某次遍历中没有元素互换位置,则不必继续遍历,因为所有元素都已经排好了。

时间复杂度O(n^2) 

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

四、归并排序

思路:将数组分为两半,递归使用归并排序后合并。

当要排序的数组长度大于一时,先创建两个新数组,分别把数组的左右两部分复制到新数组中,对新数组递归使用归并排序,然后把左右两部分数组合并起来。

public static void mergeSort(int[] nums) {
		if(nums.length > 1) {
			int leftLen = nums.length / 2;
			int[] left = new int[leftLen];
			System.arraycopy(nums, 0, left, 0, leftLen);
			mergeSort(left);
			int rightLen = nums.length - leftLen;
			int[] right = new int[rightLen];
			System.arraycopy(nums, leftLen, right, 0, rightLen);
			mergeSort(right);
			merge(left, right, nums);
		}
	}
	
	private static void merge(int[] left, int[] right, int[] temp) {
		int leftIndex = 0;
		int rightIndex = 0;
		int tempIndex = 0;
		while(leftIndex < left.length && rightIndex < right.length) {
			if(left[leftIndex] < right[rightIndex]) {
				temp[tempIndex++] = left[leftIndex++];
			}
			else {
				temp[tempIndex++] = right[rightIndex++];
			}
		}
		while(leftIndex < left.length) {
			temp[tempIndex++] = left[leftIndex++];
		}
		while(rightIndex < right.length) {
			temp[tempIndex++] = right[rightIndex++];
		}
	}

时间复杂度O(n*logn) ,空间复杂度O(n)

上述方法由于频繁开辟新空间,使得空间复杂度较高,下面给出一个改进方法,通过保存左右指针代替创建新数组。

	public static void mergeSort(int[] nums) {
		//递归外开辟空间防止频繁开辟
		int[] temp = new int[nums.length];
		mergeSort(nums, 0, nums.length - 1, temp);
	}
	
	private static void mergeSort(int[] nums, int leftIndex, int rightIndex, int[] temp) {
		if(leftIndex < rightIndex) {
			int midIndex = (leftIndex + rightIndex) / 2;
			mergeSort(nums, leftIndex, midIndex, temp);
			mergeSort(nums, midIndex + 1, rightIndex, temp);
			merge(nums, leftIndex, midIndex, rightIndex, temp);
		}
	}
	
	private static void merge(int[] nums, int left, int mid, int right, int[] temp) {
		int tempIndex = 0;
		int leftIndex = left;
		int rightIndex = mid + 1;
		while(leftIndex <= mid && rightIndex <= right) {
			if(nums[leftIndex] < nums[rightIndex]) {
				temp[tempIndex++] = nums[leftIndex++];
			}
			else {
				temp[tempIndex++] = nums[rightIndex++];
			}
		}
		while(leftIndex <= mid) {
			temp[tempIndex++] = nums[leftIndex++];
		}
		while(rightIndex <= right) {
			temp[tempIndex++] = nums[rightIndex++];
		}
		tempIndex = 0;
		while(left <= right) {
			nums[left++] = temp[tempIndex++];
		}
	}

五、快速排序

思路:先在数组中寻找一个基,默认第一个元素,然后保存两个指针high和low。

由于要升序,所以high指针从最后一个元素开始遍历,当high指针大于基时向前移动,小于基时停止,此时high代表的元素小于基。low指针从第一个元素开始遍历,当low指针小于基时向后移动,大于基时停止,此时low代表元素大于基。这时low与high中元素互换,则满足要求继续上述遍历。

当low与high指针相遇时,遍历结束。此时由于是high指针先移动,所以此时指针所对应值小于基,基与指针值交换,排序结束。

public static void quickSort(int[] nums) {
		quickSort(nums, 0, nums.length - 1);
	}
	
	private static void quickSort(int[] nums, int start, int end) {
		if(end > start) {
			int base = partition(nums, start, end);
			quickSort(nums, start, base - 1);
			quickSort(nums, base + 1, end);
		}
	}
	//将base定位到正确位置,左侧小于base,右侧大于base
	private static int partition(int[] nums, int start, int end) {
		int base = nums[start];
		int low = start;
		int high = end;
		while(low < high) {
			while(low < high && nums[high] >= base) {
				high--;
			}
			while(low < high && nums[low] <= base) {
				low++;
			}
			if(low < high) {
				int temp = nums[high];
				nums[high] = nums[low];
				nums[low] = temp;
			}
		}
		nums[start] = nums[high];
		nums[high] = base;
		return high;
	}

平均时间复杂度O(n*logn),最差O(n^2) ,空间复杂度O(1)

六、堆排序

首先先构造一个堆类,它是一个完全二叉树,其中根节点大于它的子节点。

添加元素时添加到列表末尾,若比父节点大则与父节点交换位置,比父节点小则添加完毕。

删除元素时,把尾节点元素代替根节点,并删除尾节点。若此时根节点元素小于子节点,交换位置,直到根节点大于字节点。

public class Heap<E extends Comparable<? super E>> {
	private List<E> list = new ArrayList<>();
	
	public Heap() {
		
	}
	
	public Heap(E[] theList) {
		for(int i = 0; i < theList.length; i++) {
			add(theList[i]);
		}
	}
	
	public void add(E t) {
		list.add(t);
		int currentIndex = list.size() - 1;
		//判断是否为根节点
		while(currentIndex > 0) {
			int parentIndex = (currentIndex - 1) / 2;
			if(list.get(currentIndex).compareTo(list.get(parentIndex)) > 0) {
				swap(currentIndex, parentIndex);
				currentIndex = parentIndex;
			}
			else {
				break;
			}
		}
	}
	
	public E remove() {
		if(list.size() == 0) {
			return null;
		}
		E removed = list.get(0);
		list.set(0, list.get(list.size() - 1));
		list.remove(list.size() - 1);
		int currentIndex = 0;
		while(currentIndex < list.size()) {
			int leftIndex = 2 * currentIndex + 1;
			int rightIndex = 2 * currentIndex + 2;
			//左节点越界直接退出
			if(leftIndex >= list.size()) {
				break;
			}
			int maxIndex = leftIndex;
			//右节点没越界开始讨论,越界最大即左节点
			if(rightIndex < list.size()) {
				if(list.get(leftIndex).compareTo(list.get(rightIndex)) < 0) {
					maxIndex = rightIndex;
				}
			}
			if(list.get(currentIndex).compareTo(list.get(maxIndex)) < 0) {
				swap(maxIndex, currentIndex);
				currentIndex = maxIndex;
			}
			else {
				break;
			}
		}
		return removed;
	}
	
	private void swap(int currentIndex, int parentIndex) {
		E temp = list.get(parentIndex);
		list.set(parentIndex, list.get(currentIndex));
		list.set(currentIndex, temp);
	}
}

要排序数组,只需新建一个堆,把数组中数加入堆,再一个一个删除把值赋给数组即可。 

public static void heapSort(int[] nums) {
		Heap<Integer> heap = new Heap<>();
		for(int i = 0; i < nums.length; i++) {
			heap.add(nums[i]);
		}
		for(int i = nums.length - 1; i >= 0; i--) {
			nums[i] = heap.remove();
		}
	}

时间复杂度O(n*logn),空间复杂度O(1)

七、桶排序

八、基数排序

九、外部排序

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值