数据结构基础结构篇

基础结构:

  初始算法

  1.   数组
  2. 链表
  3. 列表
  4. 队列
  5. 二叉树

基础算法篇:

查找算法

  1.   二叉搜索树
  2. 平衡二叉搜索树
  3. 红黑树
  4. B树
  5. 跳表
  6. 散列表

排序算法

  1. 插入排序
  2. 希尔排序
  3. 快速排序
  4. 归并排序
  5. 堆排序
  6. 计数排序
  7. 桶排序

进阶篇

进阶数据结构

进阶算法

算法思想:

  1. 贪心
  2. 回溯
  3. 动态规划
  4. 分治

二分查找

前提:给定一个内含n个元素的有序数组A,一个待查找值target

1.设置 i =0, j = n-1

2.如果 i > j , 结束查找,没找到

3.设置m=floor((i+j)/ 2),m为中间索引,floor是向下取整

4.如果target<Am 设置 j = m-1, 跳到第2步

5. 如果Am<target设置i = m+1,跳到第2步

6. 如果Am=target,结束查找,找到了

package org.example;

public class BinarySearch {
    /*二分查找基础模板
    Params: a-待查找的升序数组
    target:待查找的目标值
    Returns:
     找到返回索引,找不到返回目标值
     */
    public static int binarySearchBasic(int[] a, int target) {
        int i = 0, j = a.length - 1;//设置指针和初值
        while (i <= j) {
            int m = (i + j) >>>1;
            if (target < a[m]) {//目标值在左边
                j = m - 1;
            } else if (a[m] < target) {//目标值在右边
                i = m + 1;
            } else {
                return m;//找到了
            }
        }
        return -1;
    }

    /*
        问题1: 为什么是i<=j意味着区间有未比较的元素,而不是i<j?
            i,j它们所指向的元素也会参与比较
        问题2:(i+j)/2 有没有问题?
       /
        同一个二进制数
        1011_1111_1111_1111_1111_1111_1111_1110

        不把最高位视为符号位,代表3221225478
        把最高位视为符号位,代表-1073741826
       /
        改为(i+j)>>>1
        无符号右移一位 可以解决数字太大的问题,也适用更多语言。

        问题3:都写成小于符号有啥好处?
            增强可读性
     */

    /*
    1 [2,3,4,5] 6 右侧没找到更差
    int i = 0, j = a.length - 1;     2
    return -1;                       1
    元素个数为4-7  循环3次   floor(log_2(4))=2+1  向下取整
    元素个数为8-15 循环4次    log_2(8)=3+1
    16-31 5次
    32-63 6次
    循环次数 L=floor(log_2(n))+1

    i<=j              L+1
    int m=(i+j)>>>1;  L
    target<[m];        L
    a[m]<target ;      L
    i=m+1;             L

    (floor(log_2(n))+1)*5+4

    (3)*5+4=19*t
    (10+1)*5+4=59*t
     */
/*
   二分查找改进版

 */
    public static int binarySearchAlternative(int[] a, int target) {
        int i = 0, j = a.length;//第一处
        while (i < j) {          //第二处
            int m = (i + j) / 2;
            if (target < a[m]) {//目标值在左边
                j = m;          //第三处
            } else if (a[m] < target) {//目标值在右边
                i = m + 1;
            } else {
                return m;//找到了
            }
        }
        return -1;
    }


    /*
    线性查找
     */
    public static int linearSearch(int[] a,int target)
    {
        for(int i=0;i<a.length;i++){
            if(a[i]==target){
                return i;
            }
        }
        return -1;
    }


    /*
    1.最差的执行情况:
    2. 假设每行语句执行时间一样

    数组个数n
    int i=0;      1
    i<a.length;   n+1
    i++;          n
    a[i]==target  n
    return -1;    1

    累加一下 3*n+3

    3*4+3=19*t
    3*1024+3=3075*t
    
     */
}
时间复杂度

计算机科学中,时间复杂度是用来衡量:一个算法的执行,随数据规模增大,而增长的时间成本

  不依赖与环境元素

如何表示时间复杂度呢?

 假设算法要处理的数据规模是n,代码总的执行行数用数f(n)来表示,例如:

  线性查找算法的函数f(n)=3*n+3

  二分查找算法的函数f(n)=(floor(log_2(n))+1)*5+4

为了对f(n)进行化简,应当抓住主要矛盾,找到一个变化趋势与之相近的表示法。

例1:f(n)=3*n+3

    g(n)=n

取c=4,在no=3之后,g(n)可以作为f(n)的渐进上界,因此表示法写作O(n)

  1. O(1)  常量时间,意味着算法时间并不随数据规模而变化
  2. O(log(n))   对数时间
  3. O(n)   线性时间,算法时间与数据规模成正比
  4. O(n*log(n))  拟线性时间
  5. O(n*n)  平方时间
  6. O(2^n)  指数时间
  7. O(n!)  复杂度最高

渐进下界:从某个常数n开始,c*g(n)总是位于f(n)下方,那么记作Ω(g(n))

渐进紧界:从某个常数n开始,f(n)总是在c1*g(n)和c2*g(n)之间,那么记作Θ(g(n))

空间复杂度

  与时间复杂度类似,一般也用大O表示法来衡量:一个算法执行随数据规模增大,而增长的额外空间成本。

二分查找性能

下面分析二分查找算法的性能

  时间复杂度

   最坏程度O(log n)

   最好情况:如果待查找元素恰好在数组中央,只要循环一次O(1)

空间复杂度

  需要常数个指针i,j,m,因此额外占用的空间是O(1)

public static int binarySearch3(int[] a,int target){
        int i=0,j=a.length;
        while(1<j-i) {
            int m = (i + j) >>> 1;
            if (target < a[m]) {
                j = m;
            } else {
                i = m;
            }
        }
        if (a[i] == target) {
            return i;
        } else {
            return -1;
        }
    }

1.左闭右开的区间,i 指向的可能是目标,而 j 指向的不是目标

2.不在循环内找出,等范围内只剩 i 时,退出循环,在循环外比较a[ j ]与target

3.循环内的平均次数减少了

4.时间复杂度θ(log(n))

二分法leftmost/rightmost
 public static int binarySearchLeftmost(int[] a, int target) {
        int i = 0, j = a.length - 1;
        int candidate = -1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            if (target < a[m]) {
                j = m - 1;
            } else if (a[m] < target) {
                i = m + 1;
            } else {
                //记录候选位置
                candidate = m;
                j = m - 1;
            }
        }
        return candidate;
    }

 public static int binarySearchLeftmost2(int[] a, int target) {
        int i = 0, j = a.length - 1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            if (target <= a[m]) {
                j = m - 1;
            } else {
                i = m + 1;
            }
        }
        return i;
    }
 public static int binarySearchRightmost(int[] a, int target) {
        int i = 0, j = a.length - 1;
        int candidate = -1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            if (target < a[m]) {
                j = m - 1;
            } else if (a[m] < target) {
                i = m + 1;
            } else {
                //记录候选位置
                candidate = m;
                i = m + 1;
            }
        }
        return candidate;
    }

 public static int binarySearchRightmost2(int[] a, int target) {
        int i = 0, j = a.length - 1;
        while (i <= j) {
            int m = (i + j) >>> 1;
            if (target < a[m]) {
                j = m - 1;
            } else {
                i = m + 1;
            }
        }
        return i - 1;
    }

二分查找法Leftmost/Rightmost应用:

  求排名 Leftmost(n)+1

  求前任 Leftmost(n)-1

  求后任 rightmost(n)+1

leetcode 35

 搜索插入位置

class Solution {
    public int searchInsert(int[] a, int target) {
        //Leftmost 有重复的也可以用
        int i=0,j=a.length-1;
        while(i<=j){
            int m=(i+j)>>>1;
            if(target<=a[m]){
                j=m-1;
            }else if(a[m]<target){
                i=m+1;
            }
        }
        return i;

    }
}

另解

class Solution {
    public int searchInsert(int[] a, int target) {
       int low=0;
       int high=a.length-1;

       while(low<=high){
        int mid=(low+high)>>>1;
        long midVal=a[mid];
        if(midVal < target){
            low=mid+1;
        }else if(midVal > target){
            high=mid-1;
        }else 
          return mid;
       }
       return low;//找不到目标值

    }
}
leetcode 34 

 在排序数组中元素的第一个和最后一个位置

class Solution {
    public int[] searchRange(int[] a, int target) {
       int x=left(a,target);
       if(x==-1){
        return new int[]{-1,-1};
       }else {
        return new int[]{x,right(a,target)};
       }
    }
    public int left(int[] a,int target){
        int i=0,j=a.length-1;
        int candicate=-1;
        while(i<=j){
            int m=(i+j)>>>1;
            if(target<a[m]){
                j=m-1;
            }else if(a[m]<target){
                i=m+1;
            }else{
                candicate=m;
                j=m-1;
            }
            
        }
        return candicate;
    }
     public int right(int[] a,int target){
        int i=0,j=a.length-1;
        int candicate=-1;
        while(i<=j){
            int m=(i+j)>>>1;
            if(target<a[m]){
                j=m-1;
            }else if(a[m]<target){
                i=m+1;
            }else{
                candicate=m;
                i=m+1;
            }
            
        }
        return candicate;
    }
}

leetcode 702

二分查找

class Solution {
    public int search(int[] nums, int target) {
    int i=0,j=nums.length;
    while(i<j){
        int m=(i+j)>>>1;
        if(target<nums[m]){
            j=m;
        }else if(nums[m]<target){
            i=m+1;
        }else{
            return m;
        }
    }
    return -1;

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值