-
八大排序算法-初学笔记
八大经典排序
1、冒泡排序
基本介绍:冒泡排序是一种交换排序
基本思想:(假定从小到大的顺序)从第一个元素开始,相邻两个数据进行比较,将小的值放在左边。第一轮:从第一个元素开始,和与其相邻的后一个元素进行比较,
若后一个比前一个小,则交换位置,否则不变,然后两个索引都向后移动一位,继续判断至最后一组数据,第二轮:从第二个元素出发,继续重复上述进行比较、换位,
再进行下一轮比较,直到最后一轮结束。
时间复杂度:O(n2),通过测试,长度为80000的数组,数组中的元素从0-800000随机赋值,运行时间为6s;
代码实现:
public static void selectX(int[] arr){ for (int i = 0; i < arr.length - 1; i++) { for (int j = i + 1; j < arr.length; j++) { if(arr[i] > arr[j]){ int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } } }
2、简单选择排序
基本介绍: 选择排序也属于内部排序;与堆排序共为选择排序
基本思想: (也是假定从小到大的顺序) 先设定第一个元素为最小,让其后边的每个元素都与其判断,保存最小的数据和最小数据的索引位置,
在一轮判断后将最小值与第一个元素做替换,再设定第二个元素为当前最小值,继续判断,直到最后一组结束。
时间复杂度:O(n2),通过测试,长度为80000的数组,数组中的元素从0-800000随机赋值,运行时间不到1s;
代码实现:
public static void select2(int[] arr) { // 80000 : 不到一秒 // 800000 : 一分钟 int minIndex = 0; int min = arr[minIndex]; for(int i = 0; i < arr.length - 1; i++) { for(int j = i + 1; j < arr.length;j++) { if(arr[j] < min) { min = arr[j]; minIndex = j; } } if(minIndex != i) { arr[minIndex] = arr[i]; arr[i] = min; } } }
3、简单插入排序
基本介绍:插入排序也属于内部排序;与希尔排序同为插入排序
基本思路:(小->大)先假定有两个数组,无序数组和有序数组,将无序数组中的一个元素与有序中的元素一一比较,找出合适的位置后插入进去。
时间复杂度: O(n2),通过测试,长度为80000的数组,数组中的元素从0-800000随机赋值,运行时间不到1s;
代码实现 :
public static void insert(int[] arr) { for(int i = 1; i < arr.length ; i++) { int insertIndex = i - 1; int insertVal = arr[i]; while(insertIndex >= 0 && insertVal < arr[insertIndex]) { //找插入的地方 arr[insertIndex + 1] = arr[insertIndex]; //逐个往后移一位, insertIndex--; } arr[insertIndex + 1] = insertVal; //找到了小的数放的位置 } }
4、希尔排序 (插入排序)
基本介绍:希尔排序是插入排序的一种。
基本思路: 希尔排序是将一个数组先分成长度/2组数据,同一组数据相邻间距为长度/2,根据交换法或位移法将数据进行交换,交换完后再将数据分为间距/2组数据,
每一组之间继续进行比较交换,直到间距为0,即为结束。
时间复杂度:O(n2) 通过测试, 交换法 8万数据 4s; 位移法 80万数据 不到1s 800万数据2s, 显然希尔位移法排序效率更高。
代码实现:
//交换法 public static void shellSort(int[] arr) { // 八万 : 4s int count = 0; int temp = 0; for(int gsp = arr.length / 2; gsp > 0; gsp /= 2) { for(int i = gsp; i < arr.length; i++) { for(int j = i - gsp; j > 0; j -= gsp) { if(arr[j] > arr[j + gsp]) { temp = arr[j]; arr[j] = arr[j + gsp]; arr[j + gsp] = temp; } } } } } //移位法 public static void shellSort2(int[] arr) { // 8000000 两秒 for(int gap = arr.length / 2; gap > 0; gap /= 2) { for(int i = gap; i < arr.length; i++) { int j = i; int temp = arr[j]; while(j - gap > 0 && temp < arr[j - gap]) { arr[j] = arr[j - gap]; j -= gap; } arr[j] = temp; } } }
5、快速排序
基本介绍:快速排序是对冒泡排序的一种改进。
基本思想:通过一趟排序,将数据分为以某个值大小为界限的两部分,再将这两部分值分别以以上方式继续分组,直到数据变为有序。此方法可以用递归调用,用空间换时间,
大大提高了排序所耗费的时间。
时间复杂度:O(n*logn) 80万数据 不到1s 800万数据2s
代码实现:
public static void quick(int[] arr,int lift,int right) { if(lift < right) { int l = lift; int r = right; int key = arr[l]; while(l < r) { while(l < r && arr[r] >= key) { r -= 1; } arr[l] = arr[r]; while(l < r && arr[l] <= key) { l += 1; } arr[r] = arr[l]; //这里可能已经完成,因此赋值需要移位** if(l < r) { arr[r--] = arr[l]; } } arr[l] = key; //左向递归 quick(arr,lift,l - 1); //右向递归 quick(arr,l + 1,right); } }
6、归并排序
基本介绍:归并排序用到了分治策略,先将数组分解成一个个小的数组,然后再将数组进行排序整合,变成一个有序的大数组。
基本思想:用递归的方法将一个长的数组分解成一个个小的数组,然后给每个分解过的数组再进行排序操作,直到完成排序。
时间复杂度:O(nlog2) 800万个数据只用了不到一秒的时间
代码实现:
//分: 提供分解数组的方法 public static void merge(int[] arr, int left, int right,int[] temp) { if(left < right) { int mid = (left + right) / 2; //左分 merge(arr, left, mid, temp); //右分 merge(arr, mid + 1, right, temp); mergeSort(arr,left,right,mid,temp); } } //治: 提供合数组的方法 public static void mergeSort(int arr[], int left, int right, int mid, int temp[]) { if(left < right) { int i = left; int j = mid + 1; //temp临时的索引 int t = 0; //三大步骤进行合并 //1.先将两个部分的数组比较后放入临时数组中,直到其中一个数组放完 while(i <= mid && j <= right) { if(arr[i] <= arr[j]) { temp[t] = arr[i]; i += 1; t += 1; }else { temp[t] = arr[j]; j += 1; t += 1; } } //2.再将另一个没放完的数组全部放入临时数组中 while(i <= mid) { temp[t] = arr[i]; t += 1; i += 1; } while(j <= right) { temp[t] = arr[j]; j += 1; t += 1; } //3.将临时数组放入原数组中 int index = left; int k = 0; while(k < t) { arr[index] = temp[k]; k += 1; index += 1; } } }
7、基数排序
基本介绍:基数排序用到了桶排序的思想,将数据的同一位进行比较,然后放入对应的桶中,再逐个取出数据。
基本思想:先创建十个桶,每个桶代表着一个数位上的数字,将所有数组数据长度设置为最大数位的值,在数组中取出同一个数位上的数字进行入桶操作,然后再逐个取出来,
分别从低位到高位进行入桶、出桶,最终就会得到有序的数组。
时间复杂度:O(n*logn) 800万 不到1s
代码实现:
public static void radixSort(int[] arr) { //获取最大数的位数 int max = 0; for(int i = 0;i < arr.length; i++) { if(max < arr[i]) { max = arr[i]; } } int maxLength = (max + "").length(); //建立桶子 二维数组 int [][] tong =new int[10][arr.length]; //建立每个桶子的数的个数 int[] inNumber = new int[10]; //执行操作 for(int i = 0,n = 1; i < maxLength; i++,n*=10) { //入桶,求出每个元素的位值,然后入桶 for(int j = 0 ; j < arr.length; j++) { int digOfElement = (arr[j] / n) % 10; tong[digOfElement][inNumber[digOfElement]] = arr[j]; inNumber[digOfElement]++;//忘了移动索引 } int index = 0; //出桶 for(int k = 0; k < inNumber.length; k++) { if(inNumber[k] != 0) { //放值 for(int l = 0; l < inNumber[k]; l++) { arr[index++] = tong[k][l]; } } inNumber[k] = 0; } }
8、堆排序
基本介绍:堆排序用到的是线性二叉数组,也属于选择排序的一种。
基本思想:先将数组调整成为一个大顶堆,然后将顶元素与最后一个元素位置交换,数组长度减一。将交换后的数组继续调整成为新的大顶堆,并再次进行交换,以此类推。
时间复杂度: O(n*logn) 800万数据不到 1s完成。
代码实现:
//编写一个堆排序 public static void heapSort(int[] arr) { System.out.println("堆排序!!"); int temp = 0; //最终代码: //1.先弄一个大顶堆,升序 for(int i = arr.length / 2 + 1; i >= 0; i--) { adjustHeap(arr, i, arr.length); } //2,交换大顶堆和最后一个元素,长度减一 //3,在剩下的数组中调整大顶堆。 for(int j = arr.length - 1; j > 0; j--) { temp = arr[j]; arr[j] = arr[0]; arr[0] = temp; adjustHeap(arr, 0, j); } } //将一个数组(二叉树),调整成一个大顶堆 /** * * @param arr[] 待调整的数组 * @param i 表示非叶子节点再数组中的索引 * @param length 表示当前数组中还有多少待调整的元素 */ public static void adjustHeap(int arr[], int i, int length) { //先取出当前元素的值 int temp = arr[i]; //开始调整 for(int k = i * 2 + 1; k < length; k = k * 2 + 1) { //从左右子节点中找到最大的那个值的索引 if(k + 1 < length && arr[k] < arr[k + 1]) { k++; } //如果子节点大于父节点,直接交换 if(arr[k] > temp) { arr[i] = arr[k]; i = k;//i 指向最大的元素k,继续循环比较 }else { break;//我们是从左到右,从下到上,不必判断左下层子节点 } } //当for循环结束后,已经将以i为父节点的局部的树变成大顶堆。 arr[i] = temp;//将temp值放到调整后的位置。 }
相关术语解释:
1、稳定:如果a原先再b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原先再b前面,而a=b,排序之后a可能会出现在b的后面;
2、内排序:所有排序操作都在内存中进行
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行
3、时间复杂度:一个算法执行所耗费的时间。
4、空间复杂度:运行一个程序所需内存的大小。
5、n: 数据规模;k:“桶的个数”。
6、In-place: 不占用额外内存;
Out-place:占用额外内存。
出处:https://www.cnblogs.com/xiongmaodada/p/XiongMaodada.html
最新更新
python爬虫及其可视化
使用python爬取豆瓣电影短评评论内容
nodejs爬虫
Python正则表达式完全指南
爬取豆瓣Top250图书数据
shp 地图文件批量添加字段
爬虫小试牛刀(爬取学校通知公告)
【python基础】函数-初识函数
【python基础】函数-返回值
HTTP请求:requests模块基础使用必知必会
SQL SERVER中递归
2个场景实例讲解GaussDB(DWS)基表统计信息估
常用的 SQL Server 关键字及其含义
动手分析SQL Server中的事务中使用的锁
openGauss内核分析:SQL by pass & 经典执行
一招教你如何高效批量导入与更新数据
天天写SQL,这些神奇的特性你知道吗?
openGauss内核分析:执行计划生成
[IM002]Navicat ODBC驱动器管理器 未发现数据
初入Sql Server 之 存储过程的简单使用
uniapp/H5 获取手机桌面壁纸 (静态壁纸)
[前端] DNS解析与优化
为什么在js中需要添加addEventListener()?
JS模块化系统
js通过Object.defineProperty() 定义和控制对象
这是目前我见过最好的跨域解决方案!
减少回流与重绘
减少回流与重绘
如何使用KrpanoToolJS在浏览器切图
performance.now() 与 Date.now() 对比