数据结构和算法(算法复杂度介绍和冒泡、选择、插入、希尔、快速、归并、基数排序分析实现)

排序介绍:

在这里插入图片描述

算法的时间复杂度:

在这里插入图片描述

时间频度概念:

在这里插入图片描述

时间复杂度:

在这里插入图片描述

平均使劲按复杂度和最坏时间复杂度:

在这里插入图片描述

空间复杂度:

排序:

冒泡排序:

在这里插入图片描述

冒泡排序思路分析:

相邻两个数比较,如果逆序,则交换,可以想象有两个指针在辅助排序过程:
在这里插入图片描述
每一趟排序,数组最大的数就会确定,就像水泡冒出
在这里插入图片描述

为什么循环次数是数组大小-1?

因为每次循环后都会排序好的一个位置,最后一个数字不用排序,就是最小的那个。

冒泡排序代码实现(优化):
public class MaoPao {
	public static void main(String[] args) {
		int[] num = new int[] { 3, 9, 5, 7, 2, 1, 8, 0, 10, -5, -10 };
		int temp;
		boolean flag = false;//用于优化,如果进行交换,则复制为true
		//由于每趟排序都会有一个位置确定,所以最多num.length趟排序即可确定所有位置
		for (int i = 0; i < num.length - 1; i++) {
			//内层循环
			//第一次循环:因为要对所有数字两两相邻比较大小,一共有num.length个数字,所以第一次需要比较 num.length - 1次
			//第二次循环:第一次循环已经确定了最大数,剩下num.length - 1个数字,所以
			//只要将除了最大数之外的其他数字两两相邻比较即可,即比较剩下数字 - 1次 = num.length - 2
			//第三次循环:同上 ,比较次数为num.length - 3
			// ...
			//由于每次比较次数递减,而外层的i刚好递增,可以利用i变量多的值来控制比较次数:num.length - 1 - i
			for (int j = 0; j < num.length - 1 - i; j++) {
				if (num[j] > num[j + 1]) {
					flag = true;
					temp = num[j + 1];
					num[j + 1] = num[j];
					num[j] = temp;
				}
			}
			if(!flag) {//在本趟中一次都没有交换过,则表示已完成排序,可以退出
				break;
			}else {
				flag = false;
			}
		}
		for (int i = 0; i < num.length; i++)
			System.out.printf("%d  ", num[i]);
	}
}
选择排序:

在这里插入图片描述

选择排序思路分析:

在这里插入图片描述

扫描二维码关注公众号,回复: 11100609 查看本文章
操作说明:

在这里插入图片描述

选择排序代码实现(优化):
public class SelectSort {

	public static void main(String[] args) {
		int[] num = new int[] { 3, 9, 5, 7, 2, 1, 8, 0, 10, -5, -10 };
		int max = 0;//最小值的下标
		int temp = 0;
		//如果每次都选一个最小的放在最前面,则需要num.length - 1 次
		for (int i = 0; i < num.length - 1; i++) {
			//假定当前下标的数字为最小值
			max = i;
			
			//从后一个开始比较
			for (int j = i + 1; j < num.length; j++) {
				if (num[j] > num[max])
					max = j;
			}
			// 如果小标发生改变,则交换
			if (max != i) {
				temp = num[max];
				num[max] = num[i];
				num[i] = temp;
			}
		}
		for (int i = 0; i < num.length; i++)
			System.out.printf("%d  ", num[i]);
	}
}
插入排序:

在这里插入图片描述

插入排序思路分析:

在这里插入图片描述

插入排序代码实现:
public class InsertSort {
	public static void main(String[] args) {
		int[] num = new int[] { 4, 1, 2, 3, -6, 8, 0, 10, -5, -10 };
		System.out.println("初始");
		System.out.println(Arrays.toString(num));
		int inserValue;
		int insertIndex;
		for(int i = 1;i<num.length;i++) {
			//记录要插入的数据
			inserValue = num[i];
			//从插入数据的前一个开始找插入的位置
			insertIndex = i - 1;
			//如果找到的位置不小于0且该位置上的数字大于要插入的数字,则该位置数据后移,继续查找
			while (insertIndex >= 0 && num[insertIndex] > inserValue) {
				//将数据右移(后移)
				num[insertIndex+1] = num[insertIndex];
//				System.out.println(Arrays.toString(num));
				insertIndex--;
			}
			//如果跳出,要么是越界要么是找到了小于等于的数字
			//如果越界,表示要插入的数字很小,直接插在头部
			//如果找到了小于等于的数字,则插在该数字的后面
			insertIndex++;
			
			num[insertIndex] = inserValue;
			System.out.println("第"+i+"轮");
			System.out.println(Arrays.toString(num));
		}
		

	}
}
希尔排序:

在这里插入图片描述

希尔排序思路分析:

首先分组,例如有十个数字,就有5组,每个组内只用两个数字,虽然在数组里的顺序不相连,但是可以用间隔来模拟,对两个数字做排序是很简单的。
在这里插入图片描述

分析间隔为2的情况:

在这里插入图片描述
可以得知,当间隔的大小为1时,希尔排序就和插入排序(冒泡排序)一致了。

希尔排序内部可以使用冒泡和插入排序:

交换法(内部冒泡排序):
public class ShellSort {
	public static void main(String[] args) {
		int[] num = new int[] { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
		shellSort(num);
	}
	public static void shellSort(int[] arr) {
		int temp = 0;
		//gap从数组长度的一半,之后再一步步减半,只要为0(int)
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			for (int i = gap; i < arr.length; i++) {
				
				//当遍历到某一组时,利用间隔在该组使用冒泡排序(倒序)
				for (int j = i - gap; j >= 0; j -= gap) {
					if (arr[j] > arr[j + gap]) {
						temp = arr[j + gap];
						arr[j + gap] = arr[j];
						arr[j] = temp;
					}
				}
			}
		}
		System.out.println(Arrays.toString(arr));
	}
}
移位法(内部插入排序):
public class ShellSort {
	public static void main(String[] args) {
		int[] num = new int[] { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
		shellSort(num);
	}
	public static void shellSort2(int[] arr) {
			for (int gap = arr.length / 2; gap > 0; gap /= 2) {
				//从第gap个元素开始,逐个对其所在的组进行直接插入排序
				for (int i = gap; i < arr.length; i++) {
					
					// 变量j用来辅助移动数据
					int j = i;
					int temp = arr[j];
					//arr[j-gap]表示组内前一个元素
					if(arr[j] < arr[j-gap]) {
						while(j-gap >=0 && temp < arr[j-gap]) {
							//移动,覆盖
							arr[j] = arr[j-gap];
							j-=gap;
						}
						//当退出while后,就给temp找到出入的位置
						arr[j] = temp;
					}
				}
			}
			System.out.println(Arrays.toString(arr));		
	}
}
快速排序:

在这里插入图片描述
图解:
首先记录第一个数字为中间值
定义一个start指向开头,end指向末尾,从末尾开始移动
在这里插入图片描述

快速排序代码实现:
public class QuickSort {

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

	public static void quickSort(int[] arr, int start, int end) {
		//递归退出条件
		if(!(start<end)) {
			return;
		}
		// 把数组中第0个数字作为标准数
		// 记录该数据
		
		int started = arr[start];

		// 记录需要排序的下标
		int low = start;
		int high = end;
		while (low < high) {
			// 从后面开始找一个小的
			// 保证high的大小比low大,不等于,这样可以保证出了while后
			// high值不越界
			// 如果后面的比标准数大于或等于,则继续向前找
			while (high > low && arr[high] >= started) {
				// 向前找
				high--;
			}
			// 找到小的
			// 开始复制到前面low的位置
			arr[low] = arr[high];

			// 保持low小于high
			// 要找一个大的等于的放到后面
			// 所以遇到小于的就要过滤
			while (low < high && arr[low] < started) {
				low++;
			}

			// 如果出来了,就说明找到了
			arr[high] = arr[low];
		}
		// 由于一开始低位向高位移动,高位向低位移动,如果能够跳出循环,则表示两个下标重合
		// 将标准数插入即可
		
		arr[low] = started;
		quickSort(arr, start, low);
		quickSort(arr, low + 1, end);
	}

}
归并排序:

在这里插入图片描述
在这里插入图片描述

归并排序思路分析:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

归并排序实现代码:
public class MergeSort {
	public static void main(String[] args) {
		int[] arr = new int[] { 8, 4, 5, 7, 1, 3, 6, 2 };
		int[] temp = new int[arr.length];
		mergeSort(arr, 0, arr.length-1, temp);
		System.out.println(Arrays.toString(arr));
	}

	// 分+合的方法
	public static void mergeSort(int[] arr, int left, int right, int[] temp) {
		
		//递归入口
		if (left < right) {
			int mid = (left + right) / 2;
			
			// 向左递归进行分解
			mergeSort(arr, left, mid, temp);

			// 向右递归进行分解
			mergeSort(arr, mid + 1, right, temp);
			
			//开始合并
			merge(arr, left, mid, right, temp);
		}
		
		//递归出口
	}

	/**
	 * 
	 * @param arr   排序的原始数组
	 * @param left  左边有序序列的初始索引
	 * @param mid   中间索引
	 * @param right 右边索引
	 * @param temp  做中转的数组
	 */
	public static void merge(int[] arr, int left, int mid, int right, int[] temp) {
		// 每次合并两个有序序列,左右各一个
		// 左边有序序列的初始索引
		int i = left;

		// 右边有序序列初始索引
		int j = mid + 1;

		// temp数组的初始化索引
		int t = 0;

		// 将左右两边有序数组的诗句按照规则填充到temp数组里
		// 直到左右边的有序序列,有一边处理完成为止
		while (i <= mid && j <= right) {
			if (arr[i] < arr[j]) {
				temp[t] = arr[i];
				i++;
			} else {
				temp[t] = arr[j];
				j++;
			}
			t++;
		}

		// 把有剩余数据的一边的数据依次全部填充到temp
//		if (i > mid && j <= right) {
		while (j <= right) {
			temp[t] = arr[j];
			t++;
			j++;
		}
//		}
//		if (i <= mid && j > right) {
		while (i <= mid) {
			temp[t] = arr[i];
			t++;
			i++;
		}
//		}

		// 将temp数组的元素拷贝到arr中
		// 注意,并不是每次都拷贝所有
		t = 0;
		int templeft = left;
		// 第一次合并templeft = 0,right = 1
		// 最后一次templeft = 0,right = 7
		while (templeft <= right) {
			arr[templeft] = temp[t];
			t++;
			templeft++;
		}
	}
}
基数排序:

在这里插入图片描述

基数排序思路分析:

在这里插入图片描述
在这里插入图片描述

基数排序的代码实现:
public class RadixSort {
	public static void main(String[] args) {
		int arr[] = { 53, 3, 542, 748, 14, 214 };
		radixSort(arr);
	}

	public static void radixSort(int[] arr) {

		// 得到数组中最大数字的位数
		int max = arr[0];
		for (int i = 0; i < arr.length; i++) {
			if (max < arr[i])
				max = arr[i];
		}
		// 获得最大数的位数
		int length = (max + "").length();

		// 定义一个二维数组,表示是个桶,每个桶就是一个一位数组
		// 说明
		// 1.二维数组包含10个一位数组
		// 2.为了防止在放入数的时候,数据溢出,则被一个一维数组,大小为arr.length
		int[][] bucket = new int[10][arr.length];

		// 为了记录每个桶中的,实际放了多少数据,我们定义一个一维数组来记录各个桶的每次放入的数据个数
		int[] bucketElementCounts = new int[10];
		
		//用于获取每个位的数字
		int n = 1;
		
		while (0 < length) {
			for (int i = 0; i < arr.length; i++) {
				int digitOfElement = arr[i] / n % 10;
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
				bucketElementCounts[digitOfElement]++;
			}
			int index = 0;
			for (int i = 0; i < bucket.length; i++) {
				for (int j = 0; j < bucketElementCounts[i]; j++) {
					arr[index] = bucket[i][j];
					index++;
				}
				//在每次使用完桶后,将桶的内容清0
				bucketElementCounts[i] = 0;
			}
			n *= 10;
			length --;
		}
		System.out.println(Arrays.toString(arr));
	}
}
发布了68 篇原创文章 · 获赞 12 · 访问量 5198

猜你喜欢

转载自blog.csdn.net/qq_40963076/article/details/105161676