数据结构与算法Day05排序

一、算法的时间复杂度

1.算法的时间频度

一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)

例如使用for(int i = 1;i <= n;i++){},那么在这条for循环中,时间频度就是执行次数,执行次数会随着n的增大而增大,那么它的时间频度就是T(n) = n+1。

而像这种i = n+1;这种语句的执行次数不会随n的增大而增大,始终T(n) = 1

2.时间复杂度 

1) 一般情况下,算法中的基本操作语句的重复执行次数是问题规模 n 的某个函数,用 T(n) 表示,若有某个辅助函数 f(n) ,使得当 n 趋近于无穷大时, T(n) / f(n) 的极限值为不等于零的常数,则称 f(n) T(n) 的同数量级函数。记作 T(n)= ( f(n) ) ,称O ( f(n) )  为算法的渐进时间复杂度,简称时间复杂度。
2) T(n) 不同,但时间复杂度可能相同。 如: T(n)=n²+7n+6 T(n)=3n²+2n+2 它们的 T(n)  不同,但时间复杂度相同,都为 O(n²)
3) 计算时间复杂度的方法:
用常数 1 代替运行时间中的所有加法常数  T(n)=n²+7n+6  => T(n)=n²+7n+1
修改后的运行次数函数中,只保留最高阶项  T(n)=n²+7n+1 => T(n) = n²
去除最高阶项的系数 T(n) = n² => T(n) = n² => O(n²)

3.常见的时间复杂度 

1) 常数阶 O(1)
2) 对数阶 O( log 2 n )
3) 线性阶 O(n)
4) 线性对数阶 O(n log 2 n )
5) 平方阶 O(n^2)
6) 立方阶 O(n^3)
7) k 次方阶 O( n^k )
8) 指数阶 O(2^n)
说明: 常见的算法时间复杂度由小到大依次为: Ο(1) Ο( log 2 n ) Ο( n) Ο( nlog 2 n ) Ο( n 2 ) Ο( n 3 ) Ο( n k ) Ο( 2 n ) ,随着问题规模 n 的不断增大,上述时间复杂度不断增大,算法的执行效率越低

二、冒泡排序算法 

1.冒泡排序实现

思路分析:
1.传入一个数组,进行两次for循环,进行判断
2.第一个for循环决定进行排序的次数,第二个for循环决定每次排序时执行的次数
3.第一轮执行完,那么数组中最大的数将会被放在数组最后一个格子中,依次类推
4.那么,每一轮执行完后,每次排序执行的次数就减少1,因为数组的格子逐渐从后填满,所以在第二个for中的判断条件为int.length-1
5.-1是因为每次当前数组格子与下一个格子数比,如果不减一,那就会取到格子外,数组越界异常
public void maopao(int[] ints){
        for(int i = 0;i < ints.length;i++){
            for (int j = 0;j < ints.length - i - 1;j++){
                if(ints[j] > ints[j + 1]){
                    int temp = ints[j];
                    ints[j] = ints[j+1];
                    ints[j+1] = temp;
                }
            }
        }
    }

 2.代码优化

思路分析:
1.如果在一轮循环中都没有进行交换,那说明此时已经排序好了,不用浪费时间,直接输出即可
2.创建一个判断标志,只要执行了交换语句,那就继续循环,没执行,直接跳出循环
public void maopaoPlus(int[] ints){
        boolean flag = false;
        for(int i = 0;i < ints.length;i++){
            for (int j = 0;j < ints.length - i - 1;j++){
                if(ints[j] > ints[j + 1]){
                    flag = true;
                    int temp = ints[j];
                    ints[j] = ints[j+1];
                    ints[j+1] = temp;
                }
            }
            if(!flag){//相当于flag == false
                break;
            }else {
                flag = false;
            }
        }
    }

三、选择排序实现

选择排序(select sorting)也是一种简单的排序方法。它的基本思想是:第一次从arr[0]~arr[n-1]中选取最小值,与arr[0]交换,第二次从arr[1]~arr[n-1]中选取最小值,与arr[1]交换,第三次从arr[2]~arr[n-1]中选取最小值,与arr[2]交换,,第i次从arr[i-1]~arr[n-1]中选取最小值,与arr[i-1]交换,…, n-1次从arr[n-2]~arr[n-1]中选取最小值,与arr[n-2]交换,总共通过n-1次,得到一个按排序码从小到大排列的有序序列。

思路分析:
1.传入一个数组
2.选择第一个元素,从数组中选择最小的元素,与其交换位置
3.选择第二个元素,从数组.length-1中选择最小的元素,与其交换位置,以此类推
public void choose(int[] ints){
        int temp = 0;//记录最小值
        int id = 0;//记录最小值索引
        for(int i = 0;i < ints.length;i++){
            temp = ints[i];
            id = i;
            for(int j = i + 1;j < ints.length;j++){
                if(temp > ints[j]){
                    temp = ints[j];
                    id = j;
                }
            }
            //交换最小值与ints[i]值
            int a = ints[id];
            ints[id] = ints[i];
            ints[i] = a;
        }
    }

四、插入排序的实现

思路分析:
1.插入排序是把传入表中的数据进行插入,完成从小到大或者从大到小排序
2.将表中的第一个数据当做有序表,其他的数据当做无序表
3.从无序表中取依次元素,(从小到大)每次取出一个元素,就在有序表中逆向比较,大的放后面,小的放前面
编程思路:
1.创建一个变量存储当前需要插入的数据,为insertVal,再创建一个变量,用于存储待插入数的插入位置,为insertIndex
2.使用for循环获取每次的待插入数,初始值为ints[1],最后取到ints[ints.length-1]
3.insertIndex初始值为有序表中的最后一个元素位置
4.使用while循环进行判断,待插入值与插入位置值进行比较,如果比他小,继续向前判断,直到到有序表的尽头
public void insert(int[] ints){
        int insertVal = 0;
        int insertIndex = 0;
        for (int i = 1;i < ints.length;i++){
            insertVal = ints[i];
            insertIndex = i - 1;
            while (insertIndex >= 0 && insertVal < ints[insertIndex]){//满足此条件就会继续向下找位置
                ints[insertIndex + 1] = ints[insertIndex];//当前对比值向后移动
                insertIndex--;//找前面的进行对比
            }
            ints[insertIndex + 1] = insertVal;
        }
    }

 五、希尔排序的实现

1.交换式

思路分析:
1.希尔排序是通过几次(取决于传入数组的长度)交换后使用简单的大小交换进行的排序
2.第一次交换:每隔i = ints.length/2个数据进行绑定,进行交换
3.往后每次交换为i/2步,直到i == 0
public void hill(int[] ints){
        //第一次的i = ints.length/2
        int count = ints.length;
        while (count != 1) {//循环让每次交换进行,直到交换步数为0
            count = count / 2;//每次让步数除2,保留整数
            for (int i = count; i < ints.length; i++) {
                for (int j = i - count; j >= 0; j -= count) {
                    if (ints[j] > ints[j + count]) {
                        int temp = ints[j];
                        ints[j] = ints[j + count];
                        ints[j + count] = temp;
                    }
                }
            }
        }
    }

2.移位式 

public static void shellSort2(int[] arr) {
		
		// 增量gap, 并逐步的缩小增量
		for (int gap = arr.length / 2; gap > 0; gap /= 2) {
			// 从第gap个元素,逐个对其所在的组进行直接插入排序
			for (int i = gap; i < arr.length; i++) {
				int j = i;
				int temp = arr[j];
				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;
				}

			}
		}
	}

六、常用排序复杂度 

相关术语解释:

1) 稳定 :如果 a 原本在 b 前面,而 a=b ,排序之后 a 仍然在 b 的前面;
2) 不稳定 :如果 a 原本在 b 的前面,而 a=b ,排序之后 a 可能会出现在 b 的后面;
3) 内排序 :所有排序操作都在内存中完成;
4) 外排序 :由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
5) 时间复杂度:  一个算法执行所耗费的时间。
6) 空间复杂度 :运行完一个程序所需内存的大小。
7) n: 数据规模
8) k: 桶”的个数
9) In-place:    不占用额外内存
10) Out-place: 占用额外内存
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值