算法通关村第三关[青铜挑战]-数组

1.线性表基础

所谓线性表就是具有相同特征数据元素的一个有限序列,其中所含元素的个数称为线性表的长度,从不同的角度看,线性表可以有不同的分类,例如:

语言实现的角度

从语言实现的角度顺序表有两种基本实现方式,一体式和分离式,如下:在这里插入图片描述
一体式结构:
图a为一体式结构,存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成个完整的顺序表对象。这种结构整体性强,易于管理。但是由于数据元素存储区域是表对象的一部分,顺序表创建后,元素存储区就固定了。C和C++都是一体式的结构。
分离式结构:
图b为分离式结构,表对象里只保存与整个表有关的信息 (即容量和元素个数)实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。Java和python是分离式结构。

存储的角度

从存储的角度看,可以分为顺序型和链表型。顺序性就是将数据存放在一段固定的区间内,此时访问元素的效率非常高,但是删除和增加元素代价比较大,如果要扩容只能整体搬迁。
而在链表型里,元素之间是通过地址依次连接的,因此访问时必须从头开始逐步向后找,因此查找效率低,而删除和增加元素非常方便,并目也不需要考虑扩容的问题。链表的堂见实现方式又有单链表,循环链表、双向链表等等等。

从访问限制的角度

栈和队列又称为访问受限的线性表,插入和删除受到了限制,只能在固定的位置进行。而Hash比较特殊其内部真正存储数据一般是数组,但是访问是通过映射来实现的,大部分材料里并不将Hash归结到线性表中,这里为了学习更紧凑,将其与队栈一起学习。线性表的知识框架如下:线性表的常见操作有初始化、求表长、增删改查等,事实上每种数据结构都至少要有这几种操作,大部分的基础算法题都是基于此扩展的。在这里插入图片描述

从扩容的角度

采用分离式结构的顺序表,若将数据区更换为存储空间更大的区域,则可以在不改变表对象的前提下对其数据存储区进行了扩充,所有使用这个表的地方都不必修改。只要程序的运行环境(计算机系统)还有空闲存储,这种表结构就不会因为满了而导致操作无法进行。人们把采用这种技术实现的顺序表称为动态顺序表,因为其容量可以在使用中动态变化。
扩充的两种策略:
第一种:每次扩充增加固定数目的存储位置,如每次扩充增加10个元素位置,这种策略可称为线性增长。特点:节省空间,但是扩充操作频繁,操作次数多。
第二种:每次扩充容量加倍,如每次扩充增加一倍存储空间。特点: 减少了扩充操作的执行次数,但可能会浪费空间资源。以空间换时间,推荐的方式。
具体到每种结构语言中的结构,实现方式千差万别。其中Java基本是扩容时加倍的方式。而在Python的官方实现中,list实现采用了如下的策略: 在建立空表(或者很小的表)时,系统分配一块能容纳8个元素的存储区;在执行插入操作(insert或append) 时,如果元素存储区满就换一块4倍大的存储区。但如果此时的表已经很大 (目前的阀值为50000),则改变策略,采用加一倍的方法。引入这种改变策略的方式是为了避免出现过多空闲的存储位置。

2.数组概念

数组是线性表最基本的结构,特点是元素是一个紧密在一起的序列,相互之间不需要记录彼此的关系就能访问
数组用索引的数字来标识每项数据在数组中的位置,索引是从 0 算起的。可以根据数组中的索引快速访问数组中的元素。在这里插入图片描述
数组有两个需要注意的点,一个是从0开始记录,也就是第一个存元素的位置是a[0],最后一个是a[length-1]。其次,数组中的元素在内存中是连续存储的,且每个元素占用相同大小的内存
在这里插入图片描述
另外需要注意的是数组空间不一定是满的,100的空间可能只用了10个位置,所以要注意数据个数的变量size和数组长度length可能不一样

2.数组基本操作

2.1 数组创建和初始化

数组动态初始化:初始化时只指定数组长度,由系统为数组分配初始值
格式:数据类型[] 数组名=new 数据类型[数组长度];
范例: int[] arr=new int[10];

数组静态初始化:初始化时就分配初始值
简化格式:数据类型[] 数组名={元素1,元素2,元素3…};
范例:int[] arr={11,22,33};
完整格式:数组类型[] 数组名=new 数据类型[]{元素1,元素2,元素3…};
范例:int[] arr=new int[]{11,22,33};

2.2 查找一个元素

    /**
     * @param arr
     * @param size 已经存放的元素容量
     * @param key  待查找的元素
     * @return
     */
    public static int findByElement(int[] arr, int size, int key) {
        for (int i = 0; i < size; i++) {
            if (arr[i] == key)
                return i;
        }
        return -1;
    }

2.3 增加一个元素

将给定的元素插入到有序数组的对应位置中,我们可以先找位置,再将其后元素整体右移,最后插入到空位置上。这里需要注意,算法必须能保证在数组的首部、尾部和中间位置插入都可以成功。

    /**
     * @param arr
     * @param size    数组已经存储的元素数量
     * @param element 待插入的元素元素
     * @return
     */
    public static int addByElementSequence(int[] arr, int size, int element) {
        //size和arr.length都表示元素的数量,都是从1开始编号
        //这里得是大于等于,是因为数组的空间要大于已经存储元素空间才可以新增一个元素
        if (size >= arr.length)
            return -1;
        //这里索引赋值size是因为,如果元素比所有的都大,就是从尾部插入的
        int index = size;
        //找到新元素的插入位置,这里size是因为要遍历每个元素
        for (int i = 0; i < size; i++) {
            if (element < arr[i]) {
                index = i;
                break;
            }
        }
        //元素后移,因为从索引size处多一个位置
        for (int j = size; j > index; j--) {
            arr[j] = arr[j - 1]; //index下标开始的元素后移一个位置
        }
        arr[index] = element;//插入数据
        return index;
    }

2.4 删除一个元素

对于删除,不能一边从后向前移动一边查找了,因为元素可能不存在所以要分为两个步骤,先从最左侧开始查是否存在元素,如果元素存在,则从该位置开始执行删除操作。例如序列是1 2 3 4 5 6 7 8 9,要删除5,则应先遍历,找到5,然后从5开始执行删除操作,也就是从6开始逐步覆盖上一个元素,最终将序列变成 1 2 3 4 6 7 8 9 [9]这个方法和增加元素一样,该方法同样要求删除序列最前、中间、最后和不存在的元素都能有效

    /**
     * 遍历数组,如果发现目标元素,则将其删除,
     * 数组的删除就是从目标元素开始,用后续元素依次覆盖前继元素
     *
     * @param arr  数组
     * @param size 数组中的元素个数
     * @param key  要删除的目标值
     */
    public static int removeByElement(int[] arr, int size, int key) {
        int index = -1;
        for (int i = 0; i < size; i++) {
            if (arr[i] == key) {
                index = i;
                break;
            }
        }
        if (index != -1) {
            for (int i = index + 1; i < size; i++)
                arr[i - 1] = arr[i];
            size--;
        }
        return size;
    }

3.单调数组问题(LeetCode896🟩)

在这里插入图片描述

    /**
     * 第一种方法,两次遍历确定,第一次确定是否递增 ,第二次确认是否递减
     *
     * @param nums
     * @return
     */
    public boolean isMonotonic(int[] nums) {
        return isSorted(nums,true)||isSorted(nums,false);
    }

    public boolean isSorted(int[] nums,boolean flag){
      for(int i=0;i<nums.length-1;i++){
        if(flag){
          if(nums[i]<nums[i+1]){
            return false;
          }
        }else{
          if(nums[i]>nums[i+1]){
            return false;
          }
        }
      }
      return true;
    }

第一种方法要遍历两次,可以一次遍历确定,通过如果递增或递减不可能都出现递增递增的相邻元素判断

    /**
     * 第二种方式,一次遍历确定
     * 如果是递增的就一定不能出现递减的相邻元素,
     * 如果出现递减的就一定不能出现递增的相邻元素。
     * @param nums
     * @return
     */
    public static boolean isMonotonic_2(int[] nums) {
        boolean inc = true, dec = true;
        int n = nums.length;
        for (int i = 0; i < n - 1; ++i) {
            if (nums[i] > nums[i + 1]) {
                inc = false;
            }
            if (nums[i] < nums[i + 1]) {
                dec = false;
            }
        }
        return inc || dec;
    }

4.数组合并(LeetCode88🟩)

在这里插入图片描述

    /**
     * 方法1:两个数组从后向前逐步合并
     *
     * @param nums1
     * @param m
     * @param nums2
     * @param n
     */
       public void merge(int[] nums1, int m, int[] nums2, int n) {
        //索引从0开始的长度
        int i=m+n-1;
        //得到索引从0开始两个数组的长度
        int len1=m-1,len2=n-1;
        //都要判断到>=0为止,因为索引0上也有元素
        while(len1>=0&&len2>=0){
            if(nums1[len1]>=nums2[len2])
                nums1[i--]=nums1[len1--];
            else
                nums1[i--]=nums2[len2--];
        }
        //假如A或者B数组还有剩余时全部补入
        while(len1!=-1) nums1[i--]=nums1[len1--];
        while(len2!=-1) nums1[i--]=nums2[len2--];
    }
    /**
     * 方法2:优化上面的方法,其实就是用了个三元砍了if
     *
     * @param nums1
     * @param m
     * @param nums2
     * @param n
     */
    public void merge(int[] nums1, int m, int[] nums2, int n) {
        //索引从0开始的长度
        int i=m+n-1;
        //得到索引从0开始两个数组的长度
        int len1=m-1,len2=n-1;
        //都要判断到>=0为止,因为索引0上也有元素
        while(len1>=0&&len2>=0){
                nums1[i--]=nums1[len1]>=nums2[len2]?nums1[len1--]:nums2[len2--];
        }
        //假如A或者B数组还有剩余时全部补入
        while(len1!=-1) nums1[i--]=nums1[len1--];
        while(len2!=-1) nums1[i--]=nums2[len2--];
    }
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值