算法通关村第三关——不简单的数组增删改查|青铜挑战

1线性表基础

1.1线性表

线性表使具有相同特征数据元素的一个有限序列,其中所含元素的个数是线性表的长度。

从语言实现的角度
顺序表有两种基本实现方式,一体式和分离式。
一体式结构,存储表信息的单元与元素存储区以连续的方式安排在一块存储区里,两部分数据的整体形成一个完整的顺序表对象。
分离式结构,表对象里只保存与整个表有关的信息(即容器和元素个数),实际数据元素存放在另一个独立的元素存储区里,通过链接与基本表对象关联。

从存储的角度
分为顺序型和链表型。
顺序型是将数据存放在固定的区间内,此时访问元素的效率很高,但删除和增加元素代价较大,如果要扩容只能整体搬迁。
链表型是元素之间是通过地址依次连结的,因此访问时必须从头开始逐步向后找,访问效率低,而删除和增加元素很方便,不需考虑扩容问题。

从访问限制的角度
栈和队列称为访问受限的线性表,插入和删除受到限制,只能在固定的位置进行。

从扩容的角度
链表、树、图扩容最灵活。数组最受限制。

1.2数组的概念

数组时元素一个紧密在一起的序列,相互之间不需要记录彼此关系就能访问。

数组用索引的数字来表示每一项数据在数组中的位置,且在大多数编程语言中,索引是从0算起的。

2数组的基本操作

2.1数组创建和初始化

int[] arr=new int[10];
int[] arr={2,5,0,4,6};

2.2查找一个元素

/**
* @param size 已经存放的元素个数
* @param key 待查找的元素
*/
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增加一个元素

将给定的元素插入到有序数组的对应位置中,可以先找位置,再将其后元素整体右移,最后插入到空位置上。

  1. 判断数组是否已满,如果满了直接返回-1
  2. 初始化index为要插入的位置size
  3. 从头遍历数组,如果element小于当前元素,则更新index为当前索引i
  4. 元素后移,使空出index位置
    1. j从size开始向index遍历
    2. 每轮将j位置的元素复制到j-1位置
  5. 当遍历结束后,index位置空出
  6. 在index位置插入新元素
  7. 返回index
/**
 * @param arr
 * @param size    数组已经存储的元素数量,从1开始编号
 * @param element 待插入的元素
 * @return
 */
public static int addByElementSequence(int[] arr,int size,int element){
    //size是从1开始编号,当size>=arr.length时,表示数组已满,不能再往里面添加元素
    if(size>=arr.length)
        return -1;
    //问题1:这里index=0或index=size-1?index需要从size开始匹配找到要插入的位置
    int index=size;
    //问题2:这里i
    for(int i=0;i<size;i++){
        if(element<arr[i]){
            index=i;
            break;
        }
    }
    //元素后移,问题3:这里j=size-1可以吗?从size开始,确保参与后移的元素范围是从index到size
    for(int j=size;j>index;j--){
        arr[j]=arr[j-1];//index下标开始的元素后移一个位置
    }
    arr[index]=element;//插入元素
    return index;
}

2.4删除一个元素

先查是否存在元素,存在再删除元素

  1. 遍历数组,找到要删除的元素的索引index
  2. 如果找到,则从index+1位置开始遍历
  3. 每轮将当前元素arr[i]复制到i-1位置,实现元素向前移动
  4. 循环结束后,最后一个元素被覆盖,所以size-1
  5. 如果没找到元素,index为-1,size不变
  6. 返回新size
/**
 * 遍历数组,如果发现目标元素,则将其删除,
 * 数组的删除就是从目标元素开始,用后续元素依次覆盖前继元素
 *
 * @param arr  数组
 * @param size 数组中的元素个数
 * @param key  要删除的目标值
 */
public 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单调数组问题

896. 单调数列

  1. 调用isSorted方法两次,一次判断递增排序,一次判断递减排序
  2. 在isSorted方法中:
    1. 通过increasing参数控制递增还是递减校验
    2. 循环比较相邻元素
    3. 若出现违反排序规则,返回false
    4. 若遍历完毕都没出现,返回true
  3. 若数组满足递增或递减排序中的任意一种,是单调数组
public boolean isMonotonic(int[] nums){
    return isSorted(nums,true)||isSorted(nums,false);
}
public boolean isSorted(int[] nums,boolean increasing){
    int n=nums.length;
    for(int i=0;i<n-1;++i){
        if(increasing){
            if(nums[i]>nums[i+1]){
                return false;
            }
        }else{
            if(nums[i]<nums[i+1]){
                return false;
            }
        }
    }
    return true;
}          

直接遍历数组,判断相邻元素是递增还是递减的。

public boolean isMonotonic2(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;
}

35. 搜索插入位置

  1. 二分查找,初始化左右边界和结果ans
  2. 当左右边界不越界时循环
    1. 计算mid中间索引
    2. 若target<=mid处元素,说明应该在mid位置或左侧,所以右边界变为mid-1,变更新结果ans
  3. 如果target>mid处元素,说明应该在mid右侧,所以左边界变为mid
  4. 循环结束后,左边界右边界越界,此时ans是应该插入的位置
  5. 返回ans
public int searchInsert(int[] nums,int target){
    int n=nums.length;
    int left=0,right=n-1,ans=n;
    while(left<=right){
        int mid=((right-left)>>1)+left;
        if(target<=nums[mid]){
            ans=mid;
            right=mid-1;
        }else{
            left=mid+1;
        }
    }
    return ans;
}         

4数组合并专题

88. 合并两个有序数组

4.1合并两个数组

public void merge(int[] nums1,int nums1_len,int[] nums2,int nums2_len){
    int i=nums1_len+nums2_len-1;
    int len1=nums1_len-1,len2=nums2_len-1;
    while(len1>=0&&len2>=0){
        if(nums1[len1]<=nums2[len2])
            nums1[i--]=nums2[len2--];
        else if(nums1[len1]>nums2[len2])
            nums1[i--]=nums1[len1--];
    }
    //假如A或B数组还有剩余
    while(len2!=-1)
        num1[i--]=num2[len2--];
    while(len1!=-1)
        num1[i--]=num2[len1--];
}

4.2合并n个数组

public int[] mergeArrays(int[][] array){
    int arrNums=array.length,arrLen;
    if(arrNums==0){
        return new int[0];
    }
    //数组长度校验
    arrLen=array[0].length;
    for(int i=1;i<arrNums;i++){
        if(arrLen!=array[i].length){
            return new int[0];
        }
    }
    //开辟新空间
    int[] result=new int[arrNums*arrLen];
    //将各个数组依次合并到一起
    for(int i=0;i<arrNums;i++){
        for(int j=0;j<arrLen;j++){
            result[i*srrLen+j]=array[i][j];
        }
    }
    Arrays.sort(result);
    return result;
}

你可能感兴趣的:(算法,java)