数据结构与算法-排序一


title: 数据结构与算法-排序一
date: 2020-06-26 14:30:45
tags:
- 数据结构与算法
- 比较排序

算法的效率表示。
  • 常数级 o(1)
  • 对数级 o(log(n))
  • 线性级 o(n)
  • 线性对数级 o(nlog(n))
  • 平方级 o(n^2)
  • 指数级 o(2^2)
简单排序(都以升序为例)

冒泡排序,选择排序

  • 1,冒泡排序法

    • 两层循环

    • 比较次数为(n-1)+(n-2)+…+2+1=n(n-1)/2,所以冒泡排序法的时间复杂度为o(n^2)

    • 不是每次比较都要交换,交换的概率为1/2,所以交换次数为n(n-1)/4;

  • 2,选择排序法

    • 两层循环
    • 比较次数为(n-1)+(n-2)+…+2+1=n(n-1)/2,所以选择排序法的时间复杂度为o(n^2)
    • 经过一层循环后挑出最值才进行交换,只交换n-1次;所以交换次数减少为o(n)
  • 3,插入排序

    • 局部有序的思想!有序部分越来越长。
    • 因为不需要全部比较,只需要和有序部分进行比较(向前比较),所以可能的比较次数减少了。不过如果是以数组形式的来表示的话,插入,移动元素时可能会浪费一些资源。
    • 比较次数减少一半为n(n-1)/4,移动次数也为n(n-1)/4;移动(复制)没有比较消耗性能!比较操作最耗费性能。插入排序的时间复杂度为o(n^2)

不同时间复杂度的排序,首先看时间复杂度;相同时间复杂度情况下,看比较次数。

效率,插入排序>选择排序>冒泡排序;所有简单排序都是稳定的。

高级排序
  • 1,希尔排序

    • 插入排序,新插入数据总是以步长1向前寻找插入位置,如果新插入数据比较小,那么需要移动的中间数据量就比较大。希尔排序可以做到不必移动所有中间数据
    • 不是一次性把数据移动到正确位置;但是每次移动都会离正确位置更近!
    • 一开始间隔较大,最后间隔为1。常见增量序列为半数增量(每次长度缩小一倍);奇数序列增量1,3,5,7,…。
    • 同一gap的分组进行插入排序,gap逐渐缩减到1。所以有**三层循环!**gap控制一层;相同gap不同分组的插入排序两层!
    • 希尔的效率因为难以证明,所以简单的认为效率为o(n^1.5)
  • 2,归并排序

    • 将原数组不断拆分到长度为1;可以用递归实现
    • 不断将已经排序的数据合并到原数组。空间复杂度比较不理想,产生了许多小的零时数组。
    • 初步理解分治策略,就是把大问题分解到小问题,再由小问题合并成大问题,有个分解的过程,这里面的**小问题可以用其他的方法解决。**小问题到大问题的合成中涉及到的问题就是合并有序的数组。
    • 先拆后排序合并,o(nlog(n))。
  • 3,快速排序

    • 适用性最广,几乎最快。
    • 一次循环就可以找到某个元素的正确位置,那么这个枢纽的选取? 取头中尾的中位数。
    • 分而治之的思想,每次选的那个元素,就可以找到它正确的位置,左小右大,那么它的位置就确定了。每次选一个元素,最终递归完成,排序完成。
    • 在拆分的过程同时排序,o(nlog(n))。
//先封装一个列表封装数据
        function ArrayList() {
            //属性
            this.array = []

            //插入,展示,交换
            ArrayList.prototype.insert = function (item) {
                this.array.push(item)
            }
            ArrayList.prototype.toString = function () {
                return this.array.join('-')
            }
            ArrayList.prototype.swap = function (a, b) {
                let temp = this.array[a]
                this.array[a] = this.array[b]
                this.array[b] = temp
            }

            //排序算法,升序
            //简单排序
            //1,冒泡排序
            ArrayList.prototype.bubblesort = function () {
                let length = this.array.length
                for (let i = 0; i < length - 1; i++) {
                    for (let j = i; j < length; j++) {
                        if (this.array[j - 1] > this.array[j]) {
                            this.swap(j - 1, j)
                        }
                    }
                }
                return this//返回对象,以方便继续调用对象方法
            }
            //2,选择排序
            ArrayList.prototype.selectionsort = function () {
                let length = this.array.length
                for (let i = 0; i < length - 1; i++) {
                    let min = i//初始最小值下标为i
                    for (let j = i; j < length; j++) {
                        if (this.array[min] > this.array[j]) {
                            min = j//循环一趟造成最小值下标j
                        }
                    }
                    this.swap(i, min)//真实最小值下标,找到才交换,所以交换次数减少
                }
                return this
            }
            //3,插入排序
            ArrayList.prototype.insertionsort = function () {
                let length = this.array.length
                for (let i = 1; i < length; i++) {//从第二个位置获取数据,向前面有序部分进行插入
                    //内层循环,获取位置i数据,和前面的数据进行比较
                    let temp = this.array[i]
                    let j = i
                    while (this.array[j - 1] > temp && j > 0) {//不确定循环次数,向前比较,寻找插入位置。与有序部分比较减少次数。
                        this.array[j] = this.array[j - 1]//元素后移
                        j--//继续往前找
                    }
                    //把temp插入到正确位置
                    this.array[j] = temp
                }
                return this
            }

            //高级排序
            //1,希尔排序
            ArrayList.prototype.shellsort = function () {
                let length = this.array.length
                //初始化增量
                let gap = Math.floor(length / 2)
                //第一层while循环减小gap
                while (gap >= 1) {
                    //第二层循环同一gap不同分组的创建
                    //i等于gap,就是从每组的第二个元素开始
                    for (i = gap; i < length; i++) {//这里是i++,因为同一gap下的不同分组的起始值是连续分布的,增量为1。
                        temp = this.array[i]
                        let j = i
                        //第三层循环同一gap同一分组的元素进行插入排序
                        while (this.array[j - gap] > temp && j > 0) {//同一gap的同一分组的不同元素增量是gap
                            this.array[j] = this.array[j - gap]
                            j -= gap
                        }
                        this.array[j] = temp
                    }
                    gap = Math.floor(gap / 2)
                }
                return this
            }
            //2,归并排序
            ArrayList.prototype.mergesort = function () {
                return this.split(0, this.array.length)

            }
            //拆分,递归实现,拆分不时间产生零时数组,只是计算好位置
            ArrayList.prototype.split = function (left, right) {
                //1,终止条件,直到数组长度为一时返回
                if (right - left < 2) return this
                let center = Math.floor((left + right) / 2)
                this.split(left, center)//左边
                this.split(center, right)右边
                return this.merge(left, center, right)//利用计算好的位置调用合并,同层次的合并,在最后一步返回就好了
            }
            //合并
            ArrayList.prototype.merge = function (left, center, right) {
                //定义两个零时空间
                let arr1 = this.array.slice(left, center)//注意是不包括结束位置的是开区间
                let arr2 = this.array.slice(center, right)
                //追加两个正无穷,这样可以减少边界值判断的复杂程度
                arr1.push(Number.MAX_SAFE_INTEGER)//用i来记录
                arr2.push(Number.MAX_SAFE_INTEGER)//用j来记录
                //i和j为两个零时数组的指针变量,k为被写入数组的位置变量
                for (let i = 0, j = 0, k = left; k < right; k++) {//因为有正无穷所以免去了ij的判断
                    // if(arr1[i]<arr2[j]){
                    //     this.array[k]=arr1[i]
                    //     i++
                    // }else{
                    //     this.array[k]=arr2[j]
                    //     j++
                    // }
                    this.array[k] = arr1[i] < arr2[j] ? arr1[i++] : arr2[j++]//合并判断赋值和变量累加
                }
                return this
            }
            //3,快速排序
            // 选择枢纽
            ArrayList.prototype.pivot = function (left, right) {
                // 1.求出中间的位置
                var center = Math.floor((left + right) / 2)

                // 2.判断并且进行交换
                if (this.array[left] > this.array[center]) {
                    this.swap(left, center)//二者大的到了中间
                }
                if (this.array[center] > this.array[right]) {
                    this.swap(center, right)//最大值到了右边
                }
                if (this.array[left] > this.array[center]) {
                    this.swap(left, center)//次大值到了中间,注意又是比较left与center!!!
                }

                // 3.将center移动到right - 1的位置;这样比较操作可以减少一个
                this.swap(center, right - 1)

                // 4.返回pivot
                return this.array[right - 1]//后面要以这个值为基准进行比较。
            }
            ArrayList.prototype.quicksort = function () {
                return this.quicksortRec(0, this.array.length - 1)//初始化调用递归并返回最终对象
            }
            ArrayList.prototype.quicksortRec = function (left, right) {
                //1,递归结束条件
                if (left >= right) return this//返回设置好的对象
                
                //2,递归函数体
                //获取枢纽
                let pivot = this.pivot(left, right)
                //指针记录变量,这里的记录的初始值的设定决定了递归调用区间的开闭性。
                let i = left + 1//left已经比right-1枢纽小
                let j = right - 2//right已经比pivot比较,而且没必要和自己比较
                while (true) {//i<=j时,继续进入循环,找下一对可以交换的。
                    while (this.array[i] < pivot) {//在左边找到比枢纽大的值停下,这里也是要循环的,如果只有if,只变一次
                        i++
                    }
                    while (this.array[j] > pivot) {//在右边找到比枢纽小的值停下
                        j--
                    }

                    if (i < j) {//等于就没必要交换,自己和自己交换没意义
                        this.swap(i, j)
                    } else {
                        break//不写这句,就算不交换也不会跳出循环
                    }
                }

                //3,递归调用
                //这里时i-1与i取决于i和j的初始值的设置;如果设置是j=right-1,那么右区间的就是从i+开始
                 this.quicksortRec(left, i-1)//左边设置完毕
                 return this.quicksortRec(i, right)//右边也设置完毕,返回该对象
            }
        }
        let list = new ArrayList()
        list.insert(66)
        list.insert(88)
        list.insert(12)
        list.insert(87)
        list.insert(100)
        list.insert(5)
        list.insert(566)
        list.insert(23)
        list.insert(99)
        //所有方法均返回设置好的对象,以便于统一调用toString方法以供展示
        console.log(list.toString())//66-88-12-87-100-566-23
        //console.log(list.bubblesort().toString())//冒泡排序12-23-66-87-88-99-100-566
        //console.log(list.selectionsort().toString())//选择排序
        //console.log(list.insertionsort().toString())//插入排序
        //console.log(list.shellsort().toString())//希尔排序
		//console.log(list.mergesort().toString())//归并排序
        console.log(list.quicksort().toString())//快速排序
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值