9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现
内部排序: 就是使用内存空间来排序
外部排序: 就是数据量很大,需要借助外部存储(文件)来排序.
直接上代码:
package com.lvcai;
public class Sort {
public static void main(String[] args) {
//排序 分为: 内部排序(使用内存来排序) , 外部排序(需要借助外部存储)
// 内部排序:
// int[] array = {100, 6, 9, 2, 1, 0, 54,23,5};
// int[] arr = new int[10000000];
// for (int i = 0; i < 10000000; i++) {
// arr[i] = (int) (Math.random() * 80000000); // 生成一个[0, 8000000) 数
// }
// int[] temp = new int[arr.length];
// long before = System.currentTimeMillis();
// //selectSort(arr); // 选择排序:8万个随机数,选择排序耗时 13755 毫秒
// //bubbleSort(arr); // 冒泡排序:8万个随机数,选择排序耗时 13804 毫秒
// //insertSort(arr); // 插入排序:8万个随机数,选择排序耗时 2381 毫秒
// //shellSort(arr); // 希尔排序:8万个随机数,选择排序耗时 8606 毫秒
// quickSort(arr,0,arr.length-1); // 快速排序:8万个随机数,选择排序耗时 16 毫秒
// //MergeSort(arr,0,arr.length-1,temp); // 归并排序:8万个随机数,选择排序耗时 23 毫秒
radixSort(arr); // 基数排序: 8万个随机数,选择排序耗时 16 毫秒
// long after = System.currentTimeMillis();
// System.out.println(after - before);
//int[] array = {100, 6, 9, 2, 1, 0, 54,23,5};
//int[] array = {8,1,2,4,5,6,7};
int[] array = {6,1,2,7,9,3,4,5,10,8};
int[] temp = new int[array.length];
print(array);
MergeSort(array,0,array.length-1,temp);
print(array);
}
//1: 选择排序: 将第一个数,和数组中其余数比较, 比较结束后, 数组第一个数就是最小的那个数, 将第二个数 和数组中其余数比较, 比较结束后也是将最小的数组放在最前面
// 测试结果: 8万个随机数,选择排序耗时8049毫秒 10万个随机数,选择排序耗时9636毫秒,
public static void selectSort(int[] array){
for (int i = 0; i < array.length - 1; i++) {
for (int j = i+1; j < array.length; j++) {
if(array[i] > array[j]){
int temp = array[j];
array[j] = array[i];
array[i] = temp;
}
}
}
}
//2: 冒泡排序: 将待排序的序列 从前向后,依次比较相邻的二个元素, 如果逆序就交换, 这样是较大的值逐渐从前向后移动, 时间复杂度 O(n^2)
//测试结果: 8万个随机数,选择排序耗时9211毫秒 10万个随机数,选择排序耗时11603毫秒,
public static void bubbleSort(int[] array){
int temp= 0;
for (int i = 0; i < array.length -1; i++) {
for(int j = 0; j < array.length -1 -i ; j++){
if(array[j] > array[j+1]){
temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
}
}
//2.1: 冒泡排序优化: 如果在某一次循环比较过程中, 没有发生交换,说明此时,已经是有序的,,就提前结束
public static void bubbleSortBetter(int[] array){
int temp= 0;
for (int i = 0; i < array.length -1; i++) {
int exchageCount = 0;//交换次数,
for(int j = 0; j < array.length -1 -i ; j++){
if(array[j] > array[j+1]){
exchageCount++;
temp = array[j+1];
array[j+1] = array[j];
array[j] = temp;
}
}
if(exchageCount == 0){
break;
}
}
}
// 3: 插入排序: 将数组第一个数看做是有序的 ,后面的数都是无序的, 对于无序数据,在已排序的数据中从后向前扫描
// 速度测试结果:
//就像打牌一样, 将第一个元素看做有序的, 其余的牌看成无序的,从无序中最左侧拿出一张牌, 从后向前扫描有序序列,寻找插入位置,找到位置后,将牌插入,,
public static void insertSort(int[] array){
//思路: 1,假设第一个元素是有序的, 从第二个元素a开始比较
// 2, 如果这个元素a ,比前一个元素b小, 就将b后移一位,(会覆盖掉a,所以先将a保存下), 之后继续从后向前扫描,和a比较, 如果比a大,那么就后移,
// 3, 当循环结束的时候, 就说明已经找到插入位置了
/**
* 比如: {3,1,0,6}
* 一: 从1,和 3比较 , 1 < 3, 所以将 3 后移, 变为{3,3,0,6}, 跳出循环,此时将第一个3 替换为1 -->{1,3,0,6}
* 二: 然后 0 和3 比较, 3向后移动->{1,3,3,6}, 0 和1比较, 1向后移动->{1,1,3,6}, 跳出循环,此时将 1替换为0,->{0,1,3,6}
* 三: 然后 6和 3比较, 不变, 和1比较,不变, 和0 比较不变. 所以最终结果为 {0,1,3,6}
*
*/
for (int i = 1; i < array.length; i++) {
int insertVal = array[i];//要插入的元素, 先保存下
int insertIndex = i-1;// 要插入元素的前一元素的索引
//insertVal < array[insertIndex] 表示 要插入的元素 如果小于,之前索引对应元素, 就将array[insertIndex] 向后移一位
while(insertIndex >= 0 && insertVal < array[insertIndex]){
array[insertIndex+1] = array[insertIndex];//就将array[insertIndex] 向后移一位,会覆盖掉要插入的元素
insertIndex--;//递减,就是从后向前 扫描已排序的序列
}
//当跳出循环的时候, 说明 比insertVal大的元素, 都依次后移了一位, 而要插入的位置 刚好就是 insertIndex+1 的位置
array[insertIndex +1] = insertVal;
}
}
//4: 希尔排序: 又叫增量递减排序,是插入排序的优化, {3,1,6,0},由于最小数0 在最后,插入排序时,0 需要从最后移动到最前,这样效率反而低.
public static void shellSort(int[] array){
/**
* 思路:{8,1,2,4,5,6,7}, 长度 7
* 1: 步长增量 7/2 = 3, 从8开始,步长为3分组. 8,4,7 1,5 2,6 对这进行插入排序,结果为 {4,1,2,7,5,6,8}
* 2: 步长增量 3/2 = 1, 从4开始,步长为1分组, 就是对{4,1,2,7,5,6,8} 进行插入排序 结果为{1,2,4,5,6,7,8}
*
* {8, 9, 1, 7, 2, 3, 5, 4, 6, 0} 长度为10
* 1: 步长增量 10/2 = 5, 从8开始,步长为5分组, 8,3 9,5 1,4 7,6 2,0 对这五组分别进行插入排序,结果为:{3,5,1,6,0,8,9,4,7,2}
* 2: 步长增量 5/2 = 2, 从3开始,步长为2分组, 3,1,0,9,7 5,6,8,4,2 对这二组 分别进行插入排序, 结果为{0,2,1,4,3,5,7,6,9,8}
* 3: 步长增量 2/2 = 1, 从0开始,步长为1分组, 0,2,1,4,3,5,7,6,9,8 就是对他进行插入排序, 结果为{0,1,2,3,4,5,6,7,8,9}
*
* 可以看出 0 最小, 在第一次分组结束后, 0 已经到了中间,减少了移动次数
*/
int temp;
for(int grp =array.length/2 ; grp>0 ; grp /=2){//步长为长度/2
for(int i = grp ; i < array.length ; i++){
for(int j = i - grp; j >= 0 ; j -= grp){
if(array[j] > array[j+grp]){ //组间元素对比,如果逆序, 就交换
temp = array[j+grp];
array[j+grp] = array[j];
array[j] = temp;
}
}
}
}
}
//5: 快速排序: 是冒泡排序的改进,是一种分区交换排序方法, 思想如下: 一趟快速排序,采用从两头向中间扫描的方法,公式交换与基准记录
// 快速排序之所以快,是因为冒泡排序时相邻二个元素,依次比较,,,而快速排序,是根据基准值,将序列变成 左侧比他小,右侧比他大,这样的比较是跳跃式的,总的比较次数是比冒泡要少
/**
* @param array 待排序列
* @param leftIndex 待排序列起始位置
* @param reightIndex 待排序列结束位置
*/
public static void quickSort(int[] array, int leftIndex, int reightIndex){
/**
* 思路: {6,1,2,7,9,3,4,5,10,8}
* 1: 将第一个数 6 作为基准值,坑位(下角标0), 向从后向前扫描,找到比6小的数 5, 5填到该坑位 {5,1,2,7,9,3,4,5,10,8},同时下角标 7,成为新的坑位
* 2: 从前向后扫描,找到一个比6 大的数:7, 将7 填到上一个坑位 -> {5,1,2,7,9,3,4,7,10,8},下角标3成为新坑位
* 3: 从7开始从后向前扫描, 找到一个比6 小的数:4, 将4填到上一个坑位,-->{5,1,2,4,9,3,4,7,10,8},下角标6成为新坑位
* 4: 从4开始从前向后扫描, 找到一个比6 大的数:9, 将9填到上一个坑位,-->{5,1,2,4,9,3,9,7,10,8},下角标4成为新坑位
* 5: 从9开始从后向前扫描,找到一个比6 小的数:3, 将3填到上一个坑位, -->{5,1,2,4,3,3,9,7,10,8},下角标5成为新坑位
* 6: 此时循环结束, 然后将 基准值:6,填到上一个坑位 -->{5,1,2,4,3,6,9,7,10,8}
* 7:此时 以6 区分 {5,1,2,4,3},{9,7,10,8} 分别进行 1-6步骤
*/
if(leftIndex >= reightIndex){
return;
}
int left = leftIndex;// 记录开始索引, 因为排序过程中 leftIndex 会移动, 所以要用第三方变量left来代替参与排序
int reight = reightIndex;//记录尾索引,
int key = array[left]; //这个是基准值,通常将第一个作为基准值, 同时left成为新坑位(下标为0)
while(left < reight){
//先从后向前扫描, 找到一个比基准值小的值, 将这个值赋填到上个left坑位, 同时reight成为新坑位
while(key <= array[reight] && left < reight){
reight--;
}
array[left] = array[reight];
//从前向后扫描, 找到一个比基准值 大的值, 将这个值赋值给,上一个reight位置(填到坑中),同时这个left成为新坑位
while( key >= array[left] && left < reight){
left++;
}
array[reight] = array[left];
}
//当这个大的循环结束的时候, left== reight , 并且这个位置就是新的坑位,需要将基准值key 填到这里
array[left] = key;
//此时该序列变成一个: left 左侧都是比array[left]小, 右侧都是比array[reight]大 的序列
// 左递归, 开始下标 leftIndex, 结束下标 left-1
quickSort(array,leftIndex,left-1);
// 右递归, 开始下标 left+1, 结束下标 reightIndex
quickSort(array,reight+1,reightIndex);
}
//6: 归并排序, 归并排序需要额外的空间,temp 大小和array一样,
/**
*
* @param array 待排序 序列
* @param begin 开始索引
* @param end 尾部索引
* @param temp 额外空间, 大小和待排序一样
*/
public static void MergeSort(int[] array,int begin,int end,int[] temp){
if(begin < end){
int mid = (begin + end)/2;
//递归 将序列不停的二分
MergeSort(array,begin,mid,temp);
MergeSort(array,mid+1,end,temp);
//合并,
merge(array,begin,mid,end,temp);
}
}
public static void merge(int[] array,int begin,int mid,int end,int[] temp){
/**
* 思路: {6,1,2,7,9,3,4,5,10,8}
* 1: 先分成 左右两部分 {6,1,2,7,9} {3,4,5,10,8},继续分
* 2: {6,1,2} {7,9} {3,4,5} {10,8},继续分
* 3: {6,1} {2} {7,9} {3,4} {5} {10,8}
*
* 左序列 排序合并过程: {6,1} {2} {7,9}
* 4.1:{6,1}排序过程: 将小的数加入到temp中 {1}, 然后发现左序列还有剩余数6, 将6 加入到temp中{1,6}, 然后将temp的数,复制到array中 {1,6,2,7,9,3,4,5,10,8}
* 4.2: 由于接下来为{2}排序, 将{1,6} 和{2} 排序: 1比2小, 1添加到temp中{1}, 2比6小,2添加到temp中{1,2}, 最后左序列还有剩余6, 6添加到temp中{1,2,6}, 将temp复制到array中{1,2,6,7,9,3,4,5,10,8}
* 4.3: 接下来为{1,2,6} {7,9} 排序合并.....{1,2,6,7,9}
*
* 4.4 右序列 排序合并过程 {3,4} {5} {10,8} ---> {3,4} {5} {8,10}. 最终变成{3,4,5,8,10}
*
* 5 将{1,2,6,7,9} {3,4,5,8,10} 排序合并,然后复制到array中,,最终结果{1,2,3,4,5,6,7,8,9,10}
*/
int i = begin;//初始化,左边序列的初始索引
int j = mid + 1; //初始化, 右边序列的初始索引
int t = 0;//temp序列的初始索引
//先把左序列 右序列的数据, 进行排序填充到temp中,直到左右两边有一边处理完毕
while(i <= mid && j <= end){
if(array[i] <= array[j]){
temp[t] = array[i];
t++;
i++;
}else{
temp[t] = array[j];
t++;
j++;
}
}
//如果左序列有剩余,就加入到temp
while(i <= mid){
temp[t] = array[i];
t++;
i++;
}
//如果右序列有剩余,就加入到temp
while(j <= end){
temp[t] = array[j];
t++;
j++;
}
//将temp已排好序的,复制到array中
t = 0;
//int tempLeft = begin;
while(begin <= end){
array[begin] = temp[t];
t++;
begin++;
}
}
public static void print(int[] array){
for (int i = 0; i < array.length; i++) {
System.out.print(array[i]+" ");
}
System.out.println();
}
}
7;基数排序: 也叫桶排序,思路分析
代码:
//7: 基数排序, 也叫桶排序, 由于单个数字只有0-9, 需要有10个桶,编号从0-9,
public static void radixSort(int[] array){
/**
* 思路: 基数排序, 也叫桶排序, 思路请看图:
*/
//1, 获取数组中的最大数
int max = array[0];
for (int i = 1; i < array.length; i++) {
if(array[i] > max){
max = array[i];
}
}
//桶排序的次数为
int maxLenght = (max+"").length();
//2: 创建一个二维数组,来表示10个桶,为避免下标越界,每个桶的大小都为array.length
int[][] bucket = new int[10][array.length];
//2.1: 需要记录每个桶中的数据个数,便于取数据,这里用一个 一维数组来表示, 比如bucketNumberCount[1] = 2, 表示编号为1 的桶有2个数据
int[] bucketNumberCount = new int[10];
for (int i = 0 , n = 1; i < maxLenght; i++ , n *= 10) {
for (int j = 0; j < array.length; j++) {
//获取对应的位数, 个位,十位,百位,千位. 当n为1时候,是个位数, n为10,是十位数, n为100是百位数
int numberDigit = array[j] / n % 10;
//根据 位数,将数放入对应的桶中,numberDigit就是对应的桶编号,bucketNumberCount这个数组初始为[0,0,0,0,0,0,0,0,0,0], bucketNumberCount[numberDigit] = 0,表示这个桶中元素个数为0,bucketNumberCount[numberDigit] = 1,说明该桶中放了一个元素
bucket[numberDigit][bucketNumberCount[numberDigit]] = array[j]; //这个数放到桶中下标为0 的位置,之后放到为1的位置
bucketNumberCount[numberDigit]++;
}
//循环结束时候,说明都放到对应的桶中了, 接下来根据桶编号0-9, 将数据取出来,放到array中
int index = 0;
for (int k = 0; k < 10; k++) {
if(bucketNumberCount[k] != 0){
//说明 编号为k的桶中有数据,数量为bucketNumberCount[k], 循环取出,存到array中
for (int h = 0; h < bucketNumberCount[k]; h++) {
array[index] = bucket[k][h];
index++;
}
}
//编号为k的桶中元素取出来后,将桶中元素清0;避免影响下次存取
bucketNumberCount[k] = 0;
}
}
}
9, java数据结构和算法: 直接插入排序, 希尔排序, 简单选择排序, 堆排序, 冒泡排序,快速排序, 归并排序, 基数排序的分析和代码实现的更多相关文章
- 八大排序算法之三选择排序—简单选择排序(Simple Selection Sort)
基本思想: 在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换:然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素 ...
- Java数据结构和算法(三)——冒泡、选择、插入排序算法
上一篇博客我们实现的数组结构是无序的,也就是纯粹按照插入顺序进行排列,那么如何进行元素排序,本篇博客我们介绍几种简单的排序算法. 1.冒泡排序 这个名词的由来很好理解,一般河水中的冒泡,水底刚冒出来的 ...
- java数据结构和算法01(数组的简单使用)
一直都对这一块没有什么想法,加上不怎么理解,只是懂个大概:最近突然感觉对数据结构和算法这块有点儿兴趣,决定还是尽量详细的看看这些结构和算法: 话说什么事数据结构和算法呢?现在我也说不上来,等我学的差不 ...
- 选择排序—简单选择排序(Simple Selection Sort)原理以及Java实现
基本思想: 在要排序的一组数中,选出最小(或者最大)的一个数与第1个位置的数交换:然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素 ...
- 常见排序算法总结:插入排序,希尔排序,冒泡排序,快速排序,简单选择排序以及java实现
今天来总结一下常用的内部排序算法.内部排序算法们需要掌握的知识点大概有:算法的原理,算法的编码实现,算法的时空复杂度的计算和记忆,何时出现最差时间复杂度,以及是否稳定,何时不稳定. 首先来总结下常用内 ...
- Java数据结构和算法(九)——高级排序
春晚好看吗?不存在的!!! 在Java数据结构和算法(三)——冒泡.选择.插入排序算法中我们介绍了三种简单的排序算法,它们的时间复杂度大O表示法都是O(N2),如果数据量少,我们还能忍受,但是数据量大 ...
- 数据结构与算法(C/C++版)【排序】
第八章<排序> 一.直接插入排序 //直接插入排序 //算法思想:每趟将一个待排的关键字按照其值的大小插入到已经排好的部分有序序列的适当位置上,直到所有待排关键字都被插入到有序序列中为 ...
- Python实现八大排序(基数排序、归并排序、堆排序、简单选择排序、直接插入排序、希尔排序、快速排序、冒泡排序)
目录 八大排序 基数排序 归并排序 堆排序 简单选择排序 直接插入排序 希尔排序 快速排序 冒泡排序 时间测试 八大排序 大概了解了一下八大排序,发现排序方法的难易程度相差很多,相应的,他们计算同一列 ...
- Java数据结构和算法(五)--希尔排序和快速排序
在前面复习了三个简单排序Java数据结构和算法(三)--三大排序--冒泡.选择.插入排序,属于算法的基础,但是效率是偏低的,所以现在 学习高级排序 插入排序存在的问题: 插入排序在逻辑把数据分为两部分 ...
随机推荐
- ZOJ3715 竞选班长求最小花费
题意: 有n个小朋友竞选班长,一号想当班长,每个人都必须选择一个人当班长,并且不可以选择自己,并且每个人都有一个权值ai,这个权值就是如果1想让这个人改变主意选择自己当班长就得给他ai个糖 ...
- 无线网络的加密方式:WEP、WPA和WPA2
目录 有线等效加密( WEP ) Wi-Fi 访问保护( WPA ) Wi-Fi 访问保护 II( WPA2 ) WPA-PSK/WPA2-PSK 无线网标准 有线等效加密( WEP ) 有线等效保密 ...
- 针对缓冲区保护技术(ASLR)的一次初探
0x01 前言 ASLR 是一种针对缓冲区溢出的安全保护技术,通过对堆.栈.共享库映射等线性区布局的随机化,通过增加攻击者预测目的地址的难度,防止攻击者直接定位攻击代码位置,达到阻止溢出攻击的目的的一 ...
- 迪杰斯特拉(Dijkstra) 最短路算法
直接看B站视频吧: https://www.bilibili.com/video/BV1QK411V7V4/
- [源码解析] 并行分布式任务队列 Celery 之 负载均衡
[源码解析] 并行分布式任务队列 Celery 之 负载均衡 目录 [源码解析] 并行分布式任务队列 Celery 之 负载均衡 0x00 摘要 0x01 负载均衡 1.1 哪几个 queue 1.1 ...
- 面试侃集合 | ArrayBlockingQueue篇
面试官:平常在工作中你都用过什么什么集合? Hydra:用过 ArrayList.HashMap,呃-没有了 面试官:好的,回家等通知吧- 不知道大家在面试中是否也有过这样的经历,工作中仅仅用过的那么 ...
- ES6学习-4 解构赋值(1)数组的解构赋值
解构赋值是ES6很大的一个提升,为我们带来了很多方便,但用不好,会使程序的可读性变差,所以用时要注意,尽量保持程序的易读性. 数组解构赋值 在JS没有支持解构赋值之前,我们声明几个变量并赋值通常都是像 ...
- 从零搭建springboot服务02-内嵌持久层框架Mybatis
愿历尽千帆,归来仍是少年 内嵌持久层框架Mybatis 1.所需依赖 <!-- Mysql驱动包 --> <dependency> <groupId>mysql&l ...
- java集合类介绍
目录 集合类简介 List ArrayList LinkedList Vector Stack Set HashSet LinkedHashSet TreeSet Map HashMap Hashta ...
- [bug] springboot 静态资源 layui.css 404
目录结构 引用路径 <link rel="stylesheet" href="../static/layui/css/layui.css" type=&q ...