读《算法导论》之排序

1.插入排序

1.1 思想

保障数组左边数字都有序,每次从右边开始往左边有序数组里插入数据保证其有序。

1.2 示意图

点击查看图片

1.3 伪代码

INSERTION_SORT(A) {
    for(j = 2 to A.length) {
        key = A[j];
        i = j - 1;
        while(i >= 1 and A[i] > A[j]) {
            A[i + 1] = A[j];
            i = i - 1;
        }
        A[i + 1] = key;
    }
}

1.4 分析

  • 时间复杂度
    最好的情况下: 最外层循环一次 O(n)
    最坏的情况下: O(n2)
  • 空间复杂度
    O(1)

2.归并排序

2.1 思想

将原数组分为两个数组,每次都取最小的一个值放入新数组中

2.2 方法

点击查看图片

2.3 伪代码

//合并操作
//假设数组A[p] - A[q] 和 A[q+1] - A[r] 两段数组都已排好序,然后将他们合并
MARGE_SORT(A, p, r) {
    if (p < r) {
        q = (p + r) / 2;
        MARGE_SORT(A, p ,q);
        MARGE_SORT(A, q + 1, r);
        MERGE(A, p, q, r); 
    }
}
MERGE(A, p, q, r) {
    n1 = q - p + 1;//第一个数组的长度
    n2 = r - q;//第二个数组的长度
    new L[1...n1]; //声明两个新数组 L,R
    new R[1...n2];
    L[n1] = oo; //哨兵
    R[n2] = oo;
    i = j = 0;
    for(for k = p to r){
        if(L[i] < R[j]) {
            A[k] = L[i];
            i++;
        } else {
            A[k] = R[j];
            j++;
        }
    }
}

2.4 分析

  • 时间复杂度
    O(n*lgn)
  • 空间复杂度
    O(n)

3.堆排序

3.1 (二叉)堆

  • 堆是一种完全二叉树
  • A[PARENT(i)] >= A[i] (最大堆)

3.2 父节点和子节点

  • 求父节点

    PARENT(i) {
        retrun [i/2];
    }  
    
  • 求左节点

    LEFT(i) {
        return i * 2;
    }  
    
  • 求右节点

    RIGHT(i) {
        RETURN i * 2 + 1;
    }
    

3.3 维护堆的性质

3.3.1 思想

我们假定A[i]节点的左子树和右子树都是最大堆,但这时A[i]有可能小于其孩子。那MAX-HEAPIFY[i]通过让A[i]的值在最大堆中逐级下降,从而遵守最大堆的性质。

3.3.2 示意图

https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1528554420684&di=0238f4db1e96b27eff4d6cb8b79c4fd6&imgtype=0&src=http%3A%2F%2Fimg.my.csdn.net%2Fuploads%2F201208%2F30%2F1346330599_5310.png

3.3.3伪代码

//维护最大堆
MAX-HEAPIFY(A, i) {
    l = LEFT[i];  
    r = RIGHT[i];
    if(l <= A.heap-size and l > A[i]) {
        largest = l;
    }
    if(r <= A.heap-size and r > A[largest]) {
        largest = r;
    }
    if(largest != i) {
        exchange(A, i, largest);
        MAX-HEAPIFY[A, largest];
    }
}  

3.4 构建最大堆

3.4.1 思想

我们发现对于一个数组A(1,n)的数组,我们发现子数组A(n/2 + 1, n)是都是堆的叶子节点。我们可以自低向上的调用MAX-HEAPIFY就可以构建一个堆。

3.4.2 演示图

点击查看图片

3.4.3 伪代码

BUILD-MAX-HEAP(A) {
    for(i = A.length / 2 down to 1) {
        MAX-HEAPIFY(A, i);
    }
}  

3.5 堆排序算法

3.5.1 思想

我们发现最大堆的根节点A[1]一定是数组中最大的元素,那么我们替换A[1]和A[n],让最大节点放置在数组最后,然后再调用MAX-HEAPIFY[1],重新维护堆,然后依次轮推。

3.5.2 演示图

点击查看图片

3.5.3 伪代码

HEAPSORT(A {
    BUILD-MAX-HEAP(A);
    for(i = A.length downto 2) {
        exchange(A, 1, i);
        A.heap-size = A.heap-size - 1;
        MAX-HEAPIFY(A,1);
    }
}  

4.快速排序

4.1 思想

快速排序的思想是,每一次排序,都是的数组A[p,r]被划分为两个子数组A[p,q-1],A[q+1,r],使得A[p,q-1]中所有的元素都比A[q]小,A[q+1,r]中的所有的元素都比A[q]大,然后再用这个想法递归的对两个子数组进行排序。

4.2 示意图

点击查看图片

4.3 伪代码

QUICKSORT(A, p, r) {
    if(p < r) {
        q = PARTITION(A, p, r)
        QUICKSORT(A, p, q - 1)
        QUICKSORT(A, q + 1, r)
    } 
}
PARTITION(A, p, r) {
    x = A[r];
    i = p - 1;
    for(j = p to r - 1) {
        if(A[j] < x) {
            i = i + 1;
            exchange(A, i, j);
        }
    }
    exchange(A, i + 1, r)
    return i + 1;
}

4.4 思考快速排序最坏的情况以及解决办法

5 思考

比较算法的最优的时间复杂度。

5.1 决策树

点击查看图片

6 计数排序

6.1 思想

假设n个需要排序的元素,每个元素都在0~k中,那么对于每一个元素x,我们只要确定了小于x的元素个数,就可以确定x的位置。

6.2 示意图

点击查看图片

6.3 伪代码

COUNTING-SORT(A, B, k) {
    new C[0, k];  
    //第一步 初始化C
    for(i = 0 to k) {
        C[i] = 0;
    }
    //第二步 统计各个元素的个数
    for(j = 1 to A.length) {
        C[A[j]] = C[A[j]] + 1;
    }
    //第三步 统计各个元素对应的比之小的元素个数
    for( i = 1 to k) {
        C[i] = C[i-1] + [i];
    }
    //第四步 将各个元素放置相应的位置
    for(j = A.length downto 0) {
        B[C[A[j]]] = A[j];
        C[A[j]] = C[A[j]]-1;
    } 
}

6.4思考

第三步为啥要倒序?

7. 基数排序

7.1 思想

把待排序的整数先按个位进行排序,再按照十位排序。。。最后得到排序好的数组

7.2 示意图

点击查看图片

7.3 伪代码

//sdasd asdas
RADIX-SORT(A, B, maxDigit) {
    final int radix = 10
    new C[0, radix];  
    for(int d = 1 to maxDigit) {
        //初始化C,置零
        INIT(C);
        for(j = 1 to A.length)  {
            int digit = GET_DIGIT[A[j], d];
            C[digit] = C[digit] + 1;
        }
        for (i = 1 to radix) {
            C[digit] = C[i - 1]  + C[i];
        }
        for(j = A.length downTo 1) {
            int digit = GET_DIGIT[A[j]];
            B[C[digit]] = A[j];
            C[digit] = C[digit] - 1;
        }
        for () {
            //把B数组的值复制给A
            //结果就是A是以个位数排序后的结果
        }
    }
}

8. 桶排序

8.1 想法

假设所有需要排序的数组A[n]服从均匀分布,且所有的数都在一个小的整数区间内。

8.2 示意图

点击查看图片

8.3 伪代码

BUCKET_SORT(A) {
    n = A.length;
    new B[0, n-1];
    for(i = 0 to n - 1) {
        B[i] = EMPTY_LIST;
    }
    for(i = 1 to n) {
        INSERT A[i] INTO LIST B[nA[i]]
    }
    for(int i = 0 to n - 1) {
        INSERTION_SORT(B[i])
    }
    CONCATENATE B;
}  

9 思考

  • 查找数组中的最大的数
  • 同时查找数据的最大数和最小数
  • 查询第N大的数
  • map reduce 思想
  • bitMap 布隆过滤(缓存穿透)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值