排序算法详细总结

排序算法思维导图

1、冒泡排序

1.1算法思想

冒泡排序算法每次比较相邻的两个数,如果左边的数小于右边的数,那么交换这两个元素,否则不交换,经过第一趟交换后,最大的元素肯定在最右边的位置上,然后对前n−1个元素重复上述操作。

1.2代码

	public static void bubbleSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}//合法性检测
		/*外层循环控制要比较的范围,范围每次减少1,第一次范围为arr.length
		内层循环在范围内进行比较相邻两个数的大小,
		如果前面的数大于后面的数则进行交换
		*/
		for (int i = arr.length - 1; i > 0; i--) {
			for (int j = 0; j < i; j++) {
				if (arr[j] > arr[j + 1]) {
					swap(arr, j, j + 1);
				}
			}
		}
	}

2、选择排序

2.1算法思想

选择排序在未排序的范围内找到最小值,并将最小值放在未排序范围内的第一个位置,直到未排序范围为0。

2.2代码

	public static void selectionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		/*
		外层循环控制未排序的范围,每次减一
		minIndex代表该未排序的范围内的最小值
		如果该范围内出现比minIndex更小的数的话就交换
		最后将minIndex与i位置的数进行交换
		*/
		for (int i = 0; i < arr.length - 1; i++) {
			int minIndex = i;
			for (int j = i + 1; j < arr.length; j++) {
				minIndex = arr[j] < arr[minIndex] ? j : minIndex;
			}
			swap(arr, i, minIndex);
		}
	}

3、插入排序

3.1算法思想

往有序区中插入一个元素,直到后面每个元素都插入到有序区。

3.2代码

	public static void insertionSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}/*
		外层循环控制要插入的元素,该元素正好位于有序区的外面
		内层循环:j>=0保证不越界,
		arr[j]>arr[j+1]保证该元素插入到有序区的合适位置,
		然后交换,j--
		*/
		for (int i = 1; i < arr.length; i++) {
			for (int j = i - 1; j >= 0 && arr[j] > arr[j + 1]; j--) {
				swap(arr, j, j + 1);
			}
		}
	}

3.3补充

在工程上,虽然插入排序的时间复杂度为O(n2)但,其常数项非常小,因此,在数据量很小的时候,选择插入排序,数据量<=60时。

4、快速排序

4.1算法思想

使用了分治的思想,取数组右边界位置为num,以num为分界,比num小的都放在num左边,比num大的都放在num右边,等于num的放在中间区,然后递归处理左右两边。

4.2代码

	public static void quickSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		quickSort(arr, 0, arr.length - 1);
	}

	public static void quickSort(int[] arr, int l, int r) {
		if (l < r) {
			//swap(arr, l + (int) (Math.random() * (r - l + 1)), r);//该步骤是生成随机的右边界
			int[] p = partition(arr, l, r);//p[0]是中间区的左边界
			quickSort(arr, l, p[0] - 1);p[1]是中间区的右边界
			quickSort(arr, p[1] + 1, r);
		}
	}

	public static int[] partition(int[] arr, int l, int r) {
		int less = l - 1;
		int more = r + 1;
		int num = arr[r];
		while (l < more) {
			if (arr[l] < num) {
				swap(arr, ++less, l++);
			} else if (arr[l] > num) {
				swap(arr, --more, l);
			} else {
				l++;
			}
		}
		//swap(arr, more, l);
		return new int[] { less + 1, more - 1 };
	}

4.3补充

快速排序虽然时间复杂度达到了O(nlogn),但是他是不稳定的算法,在工程中,需要排序的元素为基本数据类型时,采用快速排序。普通的快速排序与数据状况有关,因此工程上常用随机快速排序,即x不选用中间位置,而是随机产生一个数组中某一个位置的元素,可以规避数据状况。

5、归并排序

5.1算法思想

要从l到r上排序,就排序好l到mid,mid+1到r。然后合并左右两边。
递归处理左右两边。

5.2代码

	//重载
	public static void mergeSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		mergeSort(arr, 0, arr.length - 1);
	}
	
	public static void mergeSort(int[] arr, int l, int r) {
		if (l == r) {
			return;//只有一个元素的时候直接返回
		}
		int mid = l + ((r - l) >> 1);
		//递归处理左右两边
		mergeSort(arr, l, mid);
		mergeSort(arr, mid + 1, r);
		//合并左右两边的数组
		merge(arr, l, mid, r);
	}

	public static void merge(int[] arr, int l, int m, int r) {
		int[] help = new int[r - l + 1];//辅助数组,大小为r-l+1
		int i = 0;//i指向help数组的0位置
		int p1 = l;//p1指向左边数组的第一个位置
		int p2 = m + 1;//p2指向右边数组的第一个位置
		/*
		当p1和p2有一个遍历完自己的数组时,必然有且只有一个没有遍历完
		只需要将没有遍历完的数组黏合在help数组后。
  		*/
		while (p1 <= m && p2 <= r) {
			help[i++] = arr[p1] < arr[p2] ? arr[p1++] : arr[p2++];
		}
		while (p1 <= m) {
			help[i++] = arr[p1++];
		}
		while (p2 <= r) {
			help[i++] = arr[p2++];
		}
		//因为merge的时候在l到r上,所以排好序后应该从arr[l+1]处放回。
		for (i = 0; i < help.length; i++) {
			arr[l + i] = help[i];
		}
	}

5.3补充

虽然归并排序也是O(nlogn),但是要额外使用一个数组,不过归并排序是稳定的算法。因此在工程上,如果要排序的元素为POJO类型时,选用归并排序,为基本数据类型时,选用快速排序(不需要额外数组)。

6、堆排序

6.1算法思想

构建大根堆heapInsert,然后将根结点放在数组最后一个位置,最后一个位置就排好序了,然后将前面位置的重新构建大根堆heapify,重复上述过程。

6.2代码

	public static void heapSort(int[] arr) {
		if (arr == null || arr.length < 2) {
			return;
		}
		for (int i = 0; i < arr.length; i++) {
			heapInsert(arr, i);
		}
		int size = arr.length;
		swap(arr, 0, --size);
		while (size > 0) {
			heapify(arr, 0, size);
			swap(arr, 0, --size);
		}
	}
	/*
	构建大根堆,index的父节点为(index-1)/2
	*/
	public static void heapInsert(int[] arr, int index) {
		while (arr[index] > arr[(index - 1) / 2]) {
			swap(arr, index, (index - 1) / 2);
			index = (index - 1) / 2;
		}
	}
	/*
	heapify()用来形成,在数组arr上,使index位置上的元素发生变化,
	为了重新构建大根堆,就要使index与左右孩子比较,如果小就下沉,大就跳出
	heapSize表示从0到heapSize-1范围上已经形成大根堆
	*/
	public static void heapify(int[] arr, int index, int heapSize) {
		int left = index * 2 + 1;//index的左孩子
		while (left < heapSize) {//保证左孩子存在
			int largest;
			if(left + 1 < heapSize)//如果右孩子存在
				largest = arr[left + 1] > arr[left] ? left + 1 : left;
			largest = arr[largest] > arr[index] ? largest : index;
			if (largest == index) {//如果比左右两个孩子都大,
				break;			   //直接跳出循环
			}
			swap(arr, largest, index);
			index = largest;
			left = index * 2 + 1;
		}
	}

	public static void swap(int[] arr, int i, int j) {
		int tmp = arr[i];
		arr[i] = arr[j];
		arr[j] = tmp;
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值