数据结构与算法-冒泡排序,选择排序,插入排序,希尔排序,快速排序,基数排序,归并排序,堆排序

冒泡排序

    通过对待排序序列从前向后(从下标较小的元素开始),依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后移(由小到大排序,大的数往后面移,小的数往前移)
在这里插入图片描述

public static void BubbleSort(int[] array) {
    
    
	int temp;
	boolean isProcess;
	// n个数进行n-1趟排序,n-1个数确定了最后一个就确定了
	for (int i = 0; i < array.length-1; i++) {
    
    
		isProcess = false;
		for (int j = 0; j < array.length-i-1; j++) {
    
       //最后的倒数第i+1个不用比较
			if (array[j] > array[j+1]) {
    
       //交换
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] = temp;
				isProcess = true;
			}
		}
		if (!isProcess) {
    
    
			break;
		}
	}
}

选择排序

    第一次从 array[0] ~ array[n-1] 选取最小值,与 array[0] 交换。
    第二次从 array[1] ~ array[n-1] 选取最小值,与 array[1] 交换。
    第 i 次从 array[i-1] ~ array[n-1] 选取最小值,与 array[i-1] 交换。
    以此类推,共执行 n-1 次。
在这里插入图片描述

public static void SelectionSort(int[] array) {
    
    
	int min, index;
	for (int i = 0; i < array.length-1; i++) {
    
    
		min = array[i];
		index = i;
		for (int j = i + 1; j < array.length; j++) {
    
    
			if (array[j] < min) {
    
    
				min = array[j];
				index = j;
			}
		}
		if (index != i) {
    
    
			array[index] = array[i];
			array[i] = min;
		}
	}
}

插入排序

    把n个待排序的元素看做成一个有序表和一个无序表,开始时有序表中只含有1个元素,无序表中包含n-1个元素。排序过程中,每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表。
在这里插入图片描述

public static void InsertSort(int[] array) {
    
    
	int temp, index;
	for (int i = 1; i < array.length; i++) {
    
    
		index = i;
		temp = array[index];
		while (index > 0 && temp < array[index - 1]) {
    
    
		// 前面的数比当前数大,则当前数继续往前移
			array[index] = array[index - 1];
			index--;
		}
		array[index] = temp;
	}
}

希尔排序

    将数组按一定增量分组,对每组使用直接插入算法排序;随着增量逐渐减少,每组包含的数越来越多,当增量减至1时,整个文件被分成一组。

public static void ShellSort(int[] array) {
    
    
	int increment = array.length, temp, index;
	while ((increment /= 2) != 0) {
    
    
		for (int i = increment; i < array.length; i++) {
    
    
			index = i;
			temp = array[index];
			while (index - increment >= 0 && temp < array[index - increment]) {
    
    
				array[index] = array[index - increment];
				index-=increment;
			}
			array[index] = temp;
		}
	}
}

快速排序

    快速排序的基本思想是:(挖坑填数+分治法)

  1. 先从数列中取出一个数作为基准数。
  2. 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  3. 再对左右区间重复第二步,直到各区间只有一个数。
public static void QuickSort(int[] array, int low, int high) {
    
    
	if (low < high) {
    
    
		int temp = array[low], i = low, j = high;
		//选取temp为基准,array[low]可以看成一个坑
		while (i < j) {
    
    
			while (i < j && temp <= array[j])
				j--;   //从右向左找小于temp的数来填坑
			if (i < j)
				array[i++] = array[j];   //填坑array[i],形成新坑array[j]
			while (i < j && temp >= array[i])
				i++;   //从左向右找大于temp的数来填坑
			if (i < j)
				array[j--] = array[i];   //填坑array[j],形成新坑array[i]
		}
		//退出循环时,i等于j,将temp填到最后形成的坑
		array[i] = temp;
		//此时temp左边的均小于temp,temp右边的均大于temp
		//分别递归对temp左右排序
		QuickSort(array, low, i-1);
		QuickSort(array, i+1, high);
	}
}

QuickSort(array,0,array.length-1);

基数排序

    基数排序的主要步骤:

  1. 首先,创建十个桶,用来辅助排序
  2. 先排个位数,根据个位数的值将数据放到对应下标值的桶中
  3. 排完后,我们将桶中的数据依次取出
  4. 接下来,排十位数、排百位数…
    在这里插入图片描述
public static void RadixSort(int[] array) {
    
    
	int[][] bucket = new int[10][array.length];   //10个基数(桶)
	int[] count = new int[10];   //记录每个桶有多少个数
	int max = array[0], num = 1, position, index;
	for(int i = 1; i < array.length; i++) {
    
       //找最大值的位数
		if (array[i] > max) {
    
    
			max = array[i];
		}
	}
	while (max != 0) {
    
    
		for (int i = 0; i < array.length; i++) {
    
    
			position = array[i] / num % 10;   //获取当前数的对应位
			bucket[position][count[position]++] = array[i];    //放入桶中
		}
		index = 0;
		for(int i = 0; i < bucket.length; i++) {
    
    
			if (count[i] != 0) {
    
    
				for(int j = 0; j < count[i]; j++) {
    
    
					array[index++]=bucket[i][j];  //放回原数组
				}
			}
			count[i] = 0;
		}
		num*=10;
		max/=10;
	}
}

归并排序

    归并排序的主要思想是分治法。主要过程是:(先递归后合并)

  1. 将n个元素从中间切开,分成两部分。(两部分不一定数量相等)
  2. 将步骤1分成的两部分,再分别进行递归分解。直到所有部分的元素个数都为1。
  3. 从最底层开始逐步合并两个排好序的数列。
    在这里插入图片描述
public static void merge(int[] array, int low, int mid, int high, int[] temp) {
    
    
	int first = low, end1 = mid; // first是第一组的起点,end1是第一组的终点
	int second = mid + 1, end2 = high; // second是第二组的起点,end2是第二组的终点
	int index = 0; // 临时数组下标
	while (first <= end1 && second <= end2) {
    
       //将两个有序序列循环比较,填入数组temp
		if (array[first] >= array[second])
			temp[index++] = array[second++];
		else
			temp[index++] = array[first++];
	}
	while (first <= end1)     //如果比较完毕,第一组还有数剩下,则全部填入temp
		temp[index++] = array[first++];
	while (second <= end2)     //如果比较完毕,第二组还有数剩下,则全部填入temp
		temp[index++] = array[second++];
	for(int i = 0; i < index; i++)     //将排序好的数填回原数组
		array[low + i] = temp[i];
}

public static void MergeSort(int[] array, int low, int high, int[] temp) {
    
    
	if (low < high) {
    
    
		int mid = (low + high) / 2;
		MergeSort(array, low, mid, temp);
		MergeSort(array, mid +1, high, temp);
		merge(array, low, mid, high, temp);
	}
}

int[] temp=new int[array.length];
MergeSort(array,0,array.length-1);

 堆排序

    节点的父节点为 n/2-1,节点的左子节点为 2*n+1,节点的右子节点为 2*n+2
    调整为大顶堆
在这里插入图片描述
    下面的最终结果是58,106,121,125,145,156,174,182,195,199.
在这里插入图片描述

public static void tranfer(int[] array, int index, int endIndex) {
    
    
	int temp = array[index];   //先取出当前元素
	for (int i = 2 * index + 1; i < endIndex; i = 2 * i + 1) {
    
       //从当前结点的左子结点开始
		//如果存在子节点,左子结点小于右子结点,指向右子结点
		if (i + 1 < endIndex && array[i] < array[i + 1]) {
    
    
			i++;
		}
		if (array[i] > temp) {
    
       //如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)大的往上移
			array[index] = array[i];
			index = i;   //元素的位置下移
		}else {
    
    
			break;
		}
	}
	array[index] = temp;   //将temp值放到最终的位置
}

public static void HeapSort(int[] array) {
    
    
	int temp;
	for(int i = array.length / 2 - 1; i >= 0; i--) {
    
    
		//从第一个非叶子结点从下至上,从右至左调整结构
		tranfer(array, i, array.length);
	}
	//调整堆结构+交换堆顶元素与末尾元素
	for(int i = array.length - 1; i > 0; i--) {
    
    
		temp = array[0];
		array[0] = array[i];   //将堆顶元素与末尾元素进行交换
		array[i] = temp;
		tranfer(array, 0, i);   //重新对堆进行调整
	}
}

   总结

排序算法 平均时间复杂度 最好时间复杂度 最坏时间复杂度 空间复杂度 是否稳定
冒泡排序 O(n2) O(n) O(n2) O(1) 稳定
选择排序 O(n2) O(n2) O(n2) O(1) 不稳定
插入排序 O(n2) O(n) O(n2) O(1) 稳定
希尔排序 O(n1.3) O(n) O(n2) O(1) 不稳定
快速排序 O(nlog2n) O(nlog2n) O(n2) O(nlog2n) 不稳定
基数排序 O(N∗M) O(N∗M) O(N∗M) O(N+M) 稳定
归并排序 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定
堆排序 O(nlog2n) O(nlog2n) O(nlog2n) O(1) 不稳定
  1. 归并排序可以通过手摇算法将空间复杂度降到O(1),但是时间复杂度会提高。
  2. 基数排序时间复杂度为O(N*M),其中N为数据个数,M为数据位数。

猜你喜欢

转载自blog.csdn.net/H_X_P_/article/details/106029198