zoukankan      html  css  js  c++  java
  • fasd

    基本排序算法

    冒泡排序

    没什么可说的, 改进方法就是加一个标志位防止有序后重复遍历. 由于需要遍历两次, 所以时间复杂度O(N^2)

    选择排序

    外层从0开始默认outer是最小数的下标, 内存从outer+1位置开始遍历, 不稳定, 如{ 3, 3, 3, 2 }, 当比较最后一个4时, 是第一个3和2交换, 从而不稳定. 内外层遍历两次, 时间复杂度O(N^2)

    插入排序

    相当于打扑克排序, outer从1到N-1, inner从outer到N-1, 时间复杂度O(N^2)
    插入排序选择排序冒泡排序有浪费许多比较的次数
    归并排序快的是因为小范围合并为大范围时, 有序可以同过外排方式
    小组和为大组时, 组内有序没有浪费, 永远是组与组之间的比较

    希尔排序

    归并排序

    递归把一个数字分隔为两部分, T(N) = 2*T(N/2) + O(N), a= 2, b = 2, N = 1, 时间复杂度O(N*logN), 额外空间复杂度O(N)
    递归符合master公式: T(N) = a*T(N/b) + O(N^d)时间复杂度为:
    (1) log(b, a) > d --> 复杂度O(N^(log(b, a)))
    (2) log(b, a) = d --> 复杂度O(N^d * logN)
    (3) log(b, a) < d --> 复杂度O(N^d)

    快排

    递归公式同归并排序, 由于需要记录分隔点, 所以额外空间复杂度O(logN), 快排做不到稳定性, 因为partition过程做不到稳定

    1. 经典快排与数据状况有关, 这是因为分隔点选取的问题, 如{1, 2, 3, 4, 5, 6}分隔点选取最右边时每次只排序一个数字, 此时时间复杂度为O(N^2)
    2. 如果分隔点选取中位数, 则每次恰好可把数组划分为两部分, 时间复杂度为O(N*logN)
    3. 随机快排的分隔点随机选取, 把复杂度转化为与概率有关, 复杂度长期期望为O(N*logN)

    堆排

    大根堆结构重要两个函数heapInsert与heapify, 一个上浮, 一个是下沉, 优先级队列就是堆
    建立堆的时间复杂度为O(log1) + O(log2) + O(log3) + ... + O(log(N)), 收敛域O(N)

    • 算法: 插入时, 上浮, 直至没有父节点比当前节点大; 排序交换堆顶与堆未元素, 这时堆顶元素下沉, 直至当前节点比子节点都大
    • 传送门 --> 堆排

    排序补充

    1. 归并排序可以做到额外空间复杂度O(1), 有难度, 相关搜索"归并排序内部缓存法"
    2. 快排可以做到稳定性, 有难度, 不要求掌握, 相关搜索"01 stable sort"; 快排的优势是partition过程中, 时间复杂度O(N), 空间复杂度O(1)
    3. 有一道题目, 奇数放在数组的左边边, 偶数放在数组右边, 要求原始的相对次数不变, 牛客练习 --> 调整数组顺序使奇数位于偶数前面

    排序总结

    排序方法 平均情况 最好情况 最坏情况 辅助空间 稳定性
    冒泡排序 O(N^2) O(N) O(N^2) O(1) 稳定
    选择排序 O(N^2) O(N^2) O(N^2) O(1) 不稳定
    插入排序 O(N^2) O(N) O(N^2) O(1) 稳定
    希尔排序 O(N*logN) ~ O(N^2) O(N^1.3) O(N^2) O(1) 不稳定
    堆排序 O(N*logN) O(N*logN) O(N*logN) O(1) 不稳定
    归并排序 O(N*logN) O(N*logN) O(N*logN) O(N) 稳定
    快排 O(N*logN) O(N * logN) O(N^2) O(logN) ~ O(N) 不稳定

    工程中的综合排序算法

    基础类型很长时, 使用快排, 因为基础数据类型不要求稳定
    复合数据类型长度很长时, 使用归并排序, 复合数据类型要求稳定
    任何数据类型的数组长度很短(<60)时, 使用插入排序

    桶排序

    桶排序是一个逻辑概念, 具体实现方法是计算排序, 基数排序

    1. 非基于比较的排序, 与被排序的样本的实际数据状况有很大关系, 所以实际中并不经常使用
    2. 时间复杂度O(N), 额外空间复杂度O(N)
    3. 稳定

    计算排序

    基数排序

    一个有序数组A, 另一个无序数组B, 打印B中所有不在A中的数组, A数组长度为N, B数组长度为M

    • 算法1: 暴力法, 时间复杂度O(N^2)
    • 算法2: 遍历B数组, 使用二分法在A中查找相同元素, 时间复杂度O(M * logN)
    • 算法3: 把A数组进行排序, 排序最小时间复杂度O(M*logM), 使用类似外排进行排序, 总时间复杂度O(M*logM) + O(M+N)
      两个游标, p1指向A, p2指向B
      (1) A[p1] < B[p2]; p1++
      (2) A[p1] == B[p2]; p1不移动, 移动p2, 因为B数组中可能有重复数字所以只移动p2
      (3) A[p1] > B[p2]; 打印并移动p2
    • 传送门 --> 时间复杂度理解

    使用递归查找数组中的最大值

    • 算法: 二分法查找分隔数字, 返回左右数组中的最大值
      本算法中T(N) = 2*T(N/2) + O(1), a=b=2, d=0, log(2, 2)=1 > d=0, 复杂度为O(N^log(2, 2))=O(N)
    • 传送门 --> 使用递归查找最大值

    小和

    在一个数组中, 每一个数左边比当前数小的数累加起来, 叫做这个数组的小和. 求一个数字的小和. 如[1, 3, 4, 2, 5]小和为16

    • 算法: 利用归并排序, 关键步骤res += (arr[lowPtr] < arr[hightPtr] ? arr[lowPtr] * (right - hightPtr + 1) : 0);其中arr[lowPtr] * (right - hightPtr + 1) 是关键, 和归并排序一样, 没有浪费之前的比较次数
      本题递归公式T(N) = 2*T(N/2) + O(N), 时间复杂度同归并排序, 同位O(N*logN)
    • 传送门 --> 小和

    逆序对

    在一个数组中, 左边的数如果比右边的数大, 则这两个数构成一个逆序对, 请打印所有逆序对

    • 算法: 与小和相同, 只是在merge时把数组从大到小排列, 关键步骤res += (vt[leftPtr] > vt[rightPtr] ? (right - rightPtr + 1) : 0);与上述相差大小号和乘一个数区别, 目前只能统计个数, 打印有些问题
    • 传送门 --> 逆序对
    • 牛客练习 --> 数组中的逆序对

    荷兰国旗问题

    给定一个数组arr, 和一个数num, 请把小于num的数放在数组的左边, 等于num的数放在数组的中间, 大于num的数放在数组的右边, 要求额外空间复杂度O(1), 时间复杂度O(N)

    • 算法: 准备三个游标leftPtr初始指向数组边界起始位置前一个元素即left-1, rightPtr初始指向末尾后一个元素right+1, index从头到尾遍历数组比较
      (1) 比num小, index指向元素和leftPtr指向的下一元素交换, index和leftPtr同时+1
      (2) 等于num, 只把index+1
      (3) 大于num, 把index元素和rightPtr指向的元素前一个交换, rightPtr-1, 由于不确定rightPtr指向元素和num的关系, index不变
    • 传输门 --> 荷兰国旗问题

    数据流的中位数

    实质利用堆排序找中位数

    排序数组最大差值

    给定一个数组, 求排序之后相邻两数的最大差值, 要求时间复杂度O(N), 且要求使用非基于比较的排序

    • 算法: 运用桶的概念. N个数, 准备N+1个桶, 最小值放0号桶, 最大桶放N号桶放N号桶;
      三个数组, 分别记录桶是否有值, 桶内最大值, 桶内最小值; 相邻两个数最大差值可能存在于两个非空桶之间, 也可能存在于空桶之间
    • 传输门 --> 排序数组最大差值

    数据流中位数

    如何可以得到数据流中排序后的中位数

    用数组结构实现大小固定栈

    用数组结构实现大小固定队列

    • 算法: 使用三个变量: start, end, size. 目的是为了是start和end解耦合, 使start只与size有关, end只与size有关.
      插入只与m_size和m_arraySize有关, 弹出只与m_size和0有关; 弹出直接弹出m_start位置的元素, 插入直接插入到m_end位置
      m_start的变化只与m_arraySize有关; m_end的变化只与m_arraySize有关. m_start与m_end都是+1递增
    • 传输门 --> 数组实现大小固定的队列

    仅用队列结构实现栈结构

    传送门 -->

    仅用栈结构实现队列结构

    双栈返回最小值

    实现一个特殊的栈, 在实现栈的基本功能的基础上, 再实现返回栈中的最小元素的操作. 要求: 1. pop, push, getMin操作的时间复杂度都是O(1); 2. 设计的栈类型可以使用现成的栈结构

    顺时针打印矩阵

    给定一个整型矩阵matrix, 按照从外向里以顺时针的顺序依次打印出每一个数字. 例如:如果输入如下4 X 4矩阵: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 则依次打印出数字1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10.

    旋转正方形

    给定一个整型正方形矩阵matrix, 把该矩阵调整成顺时针转换90度的样子. 额外空间复杂度O(1)

    • 算法: 同顺时针打印矩阵一样, 抽象一个顺时针打印边框的函数, 从外到内依次调用这个函数.
    • 传送门 --> 旋转正方形矩阵

    之字形打印矩阵

    给定一个矩阵matrix, 按照"之"字形方式打印这个矩阵

    • 算法: 抽象一个打印斜行的矩阵, 通过一个布尔变量来判断从上到下还是从下到上, 调用函数多了些边界判断. x_1和y_1, x_2和y_2先后移动次序有很大关系, 先比较x_1再判断y_1, 先比较y_2再判断x_2,
    • 传输门 --> 之字形打印矩阵

    行列有序的矩阵中查找

    给定一个由N*M的整数型矩阵matrix和一个整数K, matrix的每一行和每一列都是排好序的. 实现一个函数, 判断K是否在matrix中. 要求时间复杂度O(N+M), 额外空间复杂度O(1)

    • 算法: 从矩阵的特性出发, 确定查找方法. 设置查找起始点设置为右上角
        若K小于右上角的数, 则不可能在当前列的下面, 左移起始点
        若K大于右上角的数, 则不可能在当前行的左边, 下移起始点
    • 传送门 --> 行列有序的矩阵中查找
    • 牛客练习 --> 二维数组中的查找

    链表面试与笔试

    链表问题往往在空间复杂度上下工夫, 时间复杂度基本是O(N)或O(N^2)
    笔试: 目的最快把问题过掉, 不追求空间复杂度
    面试: 和面试官聊空间复杂度

    分别实现反转单向链表和反转双向链表函数

    • 算法: 分别定义两个指针, next和pre, 当head不为空时, next和pre都初始化为nullptr
      (1)next指向head下一节点
      (2)利用pre与head反转单/双链表
      (3)使pre指向head
      (4)使head指向next
    • 递归反转单链表:
      (1)递归到最后一个节点, 然后依次返回
      (2)当前节点的下一节点的next指向当前节点
      (3)当前节点的next域指向nullptr
    • 传送门 --> 反转单双链表
    • 牛客练习 --> 反转链表

    判断一个链表是否是回文结构

    给定一个链表的头结点head, 判断该链表是否是会问结构. 如: 1, 2, 1返回true; 1, 2, 2, 1返回true; 1, 2, 3返回false. 如果求时间复杂度O(N), 额外空间复杂度O(1)

    • 算法1: 使用栈把元素逆序, 遍历两次列表, 第一次压栈, 第二次和栈中元素对比是否相同, 时间复杂度O(N), 空间复杂度O(N)
    • 算法2: 使用快慢指针

    单向链表划分

    将单向链表按某值划分成左边小, 中间相等, 右边大的形式

    复制含有随机指针节点的链表

    两个连续相交问题

    给定三角形ABC和一点P(x,y,z),判断点P是否在ABC内,给出思路并手写代码

    • 面积计算公式及浮点数的比较
    #include <iostream>
    #include <math.h>
    using namespace std;
    #define ABS_FLOAT_0 0.0001		// 浮点数比较
    struct point_float {
        float x;
        float y;
    };
    
    float GetTriangleSquar(const point_float pt0, const point_float pt1, const point_float pt2) {
        point_float AB, BC;
        AB.x = pt1.x - pt0.x;
        AB.y = pt1.y - pt0.y;
        BC.x = pt2.x - pt1.x;
        BC.y = pt2.y - pt1.y;
        return fabs((AB.x * BC.y - AB.y * BC.x)) / 2.0f;
    }
    
    bool IsInTriangle(const point_float A, const point_float B, const point_float C, const point_float D){
        float SABC, SADB, SBDC, SADC;
        SABC = GetTriangleSquar(A, B, C);
        SADB = GetTriangleSquar(A, D, B);
        SBDC = GetTriangleSquar(B, D, C);
        SADC = GetTriangleSquar(A, D, C);
        float SumSuqar = SADB + SBDC + SADC;
        if ((-ABS_FLOAT_0 < (SABC - SumSuqar)) && ((SABC - SumSuqar) < ABS_FLOAT_0))
            return true;
        else
            return false;
    }
    

    n个整数的无序数组,找到每个元素后面比它大的第一个数,要求时间复杂度为O(N)

    vector<int> findMax(vector<int>num)
    {
        if(num.size()==0)return num;
        vector<int>res(num.size());
        int i=0;
        stack<int>s;
        while(i<num.size())
        {
            if(s.empty()||num[s.top()]>=num[i])
                {
                s.push(i++);
                }
            else
            {
                res[s.top()]=num[i];
                s.pop();
            }
        }
        while(!s.empty())
        {
            res[s.top()]=INT_MAX;
            s.pop();
        }
        for(int i=0; i<res.size(); i++)
            cout<<res[i]<<endl;
        return res;
    }
    
  • 相关阅读:
    GRE3000速记 :苏琪与爱马仕 05
    GRE3000速记 :苏琪与爱马仕 04
    GRE3000速记 :苏琪与爱马仕 03
    GRE3000速记 :苏琪与爱马仕 02
    GRE3000速记 :苏琪与爱马仕 11
    你知道如何安全正确的关闭线程池吗?
    你知道如何安全正确的关闭线程池吗?
    WCF配置后支持通过URL进行http方式调用
    WCF配置后支持通过URL进行http方式调用
    MyCat教程【简单介绍】
  • 原文地址:https://www.cnblogs.com/hesper/p/10772539.html
Copyright © 2011-2022 走看看