LeetCode_初级算法_数组

LeetCode|初级算法_数组

题目如下:
LeetCode_初级算法_数组_第1张图片


1.1 从排序数组中删除重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

方法一 双指针

if(nums == null || nums.length==0){
	return 0;
 }
else{
    int i=0;//i:慢指针
    for(int j=1;j

双指针:用快指针j遍历数组,慢指针i按顺序存放无重复的原数组元素

方法二 方法一改进解

if(nums == null || nums.length==0){
    return 0;
}
else{
    int i=0;
    for(int j=1;j

方法三 网上看到的解

    int i=0;
    for(int n:nums){
        if(i==0||n>nums[i-1]){
            nums[i++]=n;
        }
    }
    return i;

方法四 官方最优解

    int up = 0;//慢指针
    int down = 0;//快指针
     while(down < nums.length) {
         if(nums[down] == nums[up]) {
             down++;
         }else {
             up++;
             nums[up] = nums[down];
             down++;
         }
     }
     return up + 1;

1.2 买卖股票的最佳时机 II

给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。

贪心法

int max=0;
for(int i=0;i0)
        max+=d;
}
return max;

一次遍历,只要今天价格小于明天价格就在今天买入然后明天卖出,时间复杂度O(n)

1.3 旋转数组

给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数。
说明:
·尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个问题。
·要求使用空间复杂度为 O(1) 的原地算法。

方法一 暴力

	if(nums.length<=0||k<=0){
            return;
        }
        k=k%nums.length;//k次数过多可加
        for(int i=1;i<=k;i++){
            int temp=nums[nums.length-1];
            for(int j=nums.length-1;j>0;j--){
                nums[j]=nums[j-1];            
            }
            nums[0]=temp;
        }
         return;

方法二 官方最快版

        k=k%nums.length;
        int[] temp=nums.clone();//这里clone一个新数组,空间复杂度还是O(1)不?
        //将一个数组的指定个数元素复制到另一个数组中:public static void arraycopy(Object 被复制数组,int 被复数组起始位置,Object 目标数组,int 目标数组起始位置,int 复制长单)
        System.arraycopy(temp,temp.length-k,nums,0,k);//把原数组的后半截移到前半截
        System.arraycopy(temp,0,nums,k,temp.length-k);//把原数组的前半截移到后半截
方法三 官方第三快
        int k = k % nums.length;
        if (k == 0)
            return;
        // 每一个元素的位置: (index + k) % nums.length,其中 index 为每个元素的初始下标
        int[] numsCopy = nums.clone();
        for (int i = 0; i < numsCopy.length; i++) {
            int newIdx = (i + k) % numsCopy.length;
            nums[newIdx] = numsCopy[i];
        }

方法四 官方第二快 三次反转字符串版

k=k%nums.length;
reverse(nums,0,nums.length-1);//反转整个字符串
reverse(nums,0,k-1);//反转k前的字符串
reverse(nums,k,nums.length-1);//反转字符串剩余
}
//(123456k=2,第一次654321,第二次564321,第三次561234)
void reverse(int[] arr, int left,int right){
    while(left

1.4 存在重复

给定一个整数数组,判断是否存在重复元素。
如果任何值在数组中出现至少两次,函数返回 true。如果数组中每个元素都不相同,则返回 false。

公共代码:

if(nums.length<=1){
    return false;
}

方法一 暴力法 (时间复杂度n2,超时)

	for(int i=0;i

方法二 先排序再找重复(这时候如果有重复必相邻)

   Arrays.sort(nums);
    for(int i=0;i

方法三 哈希表 HashSet 或 HashMap 利用支持快速搜索和插入操作的动态数据结构

Set numSet=new HashSet<>(nums.length);
for(int num:nums){
    if(numSet.contains(num)){
        return true;
    }
    numSet.add(num);
}
return false;

1.5 只出现一次的数字

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现两次。找出那个只出现了一次的元素。
说明:
·你的算法应该具有线性时间复杂度。 你可以不使用额外空间来实现吗?

公共:

if(nums.length==1){
    return nums[0];
}

方法一 先排序后遍历 时间复杂度?nlogn

Arrays.sort(nums);
//因为是奇数个元素的数组,所以,这样遍历肯定会漏掉最后一个元素
for(int i=0;i

方法二 异或法

int v=nums[0];
for(int i=1;i

由题知只有一个不同元素,其余都是成双相等,根据异或的性质,把所有元素全异或一遍,结果就是不同元素(相同元素异或为0消去了)

方法三 hash表法 空间复杂度O(n),不符合题目要求不用额外空间复杂度的要求

Map hashMap=new HashMap();
for(int num:nums){
    if(hashMap.containsKey(num)){
        hashMap.remove(num);
    }else{
        hashMap.put(num,1);
    }
}
for(Integer key:hashMap.keySet()){
    return key;
}
return -1;//没有唯一值

1.6 两个数组的交集 II

给定两个数组,编写一个函数来计算它们的交集
说明:
·输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
·我们可以不考虑输出结果的顺序。
进阶:
·如果给定的数组已经排好序呢?你将如何优化你的算法?
·如果 nums1 的大小比 nums2 小很多,哪种方法更优?
·如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

方法一 哈希法

Map nums1Map=new HashMap();
//存nums1
for(int num:nums1){
    if(nums1Map.containsKey(num)){
        int value=nums1Map.get(num);
        nums1Map.put(num,++value);
    }else{
        nums1Map.put(num,1);
    }
}
LinkedList list=new LinkedList();//增删多选链表list
//遍历nums2
for(int num2:nums2){
    if(nums1Map.containsKey(num2)){
        int value=nums1Map.get(num2);
        if(value>0){
        nums1Map.put(num2,--value);
        list.add(num2);
        }
    }
}
int[] arr=new int[list.size()];
for(int i=0;i

利用HashMap存nums1,key-元素,value-元素次数,遍历nums2,出现一次value-1并将值存入返回数组(数组初始化大小不确定,所以先用List暂存)

方法二 数组排序过 直接从小到大对比找交集

Arrays.sort(nums1);
Arrays.sort(nums2);
LinkedList list=new LinkedList();
for(int i=0,j=0;i

如果 nums1 的大小比 nums2 小很多,我觉得选择方法二好一点吧,毕竟nums1.length小会早点出循环

1.7 加一

给定一个由整数组成的非空数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

方法一 直接从尾部遍历加一

	for(int i=digits.length-1;i>-1;i--){
            //超过单位上限9且数组下标0,则扩展数组,长度加一
            if(digits[i]+1==10 && i==0){
                int[] digits2=new int[digits.length+1];
                digits2[0]=1;//进位后首位肯定是1
                // digits2[1]=0;
                // for(int j=2;j

方法二 方法一简化改进版

for(int i=digits.length-1;i>-1;i--){
    digits[i]++;
    digits[i]=digits[i]%10; 
    if(digits[i]!=0) return digits;
}
digits=new int[digits.length+1];
digits[0]=1;
return digits;

1.8 移动零

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

方法一 双指针法

int i=0;
//遍历数组,调整元素顺序
for(int j=0;j

长的遍历数组,短的保存遍历到的非零元素,尾巴填充0

1.9 两数之和

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。

方法一 暴力法

for(int i=0;i

方法二 哈希表法

	Map map = new HashMap<>();
        for (int i = 0; i < nums.length; i++) {
            map.put(nums[i], i);
        }
        for (int i = 0; i < nums.length; i++) {
            int complement = target - nums[i];
            if (map.containsKey(complement) && map.get(complement) != i) {
                return new int[] { i, map.get(complement) };
            }
        }
        throw new IllegalArgumentException("No two sum solution");

空间换时间,时间复杂度由暴力的O(n2)降到O(n),不过空间复杂度由暴力的O(1)升到O(n)

方法三 改进方法二,第一遍遍历数组转为哈希的同时查找有无解

Map map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
    if(map.containsKey(target-nums[i])){
        return new int[]{i,map.get(target-nums[i])};
    }
    map.put(nums[i], i);
}
throw new IllegalArgumentException("No twoSum");

1.10 有效的数独

判断一个 9x9 的数独是否有效。只需要根据以下规则,验证已经填入的数字是否有效即可。
1.数字 1-9 在每一行只能出现一次。
2.数字 1-9 在每一列只能出现一次。
3.数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
LeetCode_初级算法_数组_第2张图片
上图是一个部分填充的有效的数独。
数独部分空格内已填入了数字,空白格用 ‘.’ 表示。
说明:
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
给定数独序列只包含数字 1-9 和字符 ‘.’ 。
给定数独永远是 9x9 形式的。

方法一 暴力法 遍历循环 每行 每列 每个小九宫 利用Set判断唯一性

for(int i=0;i<9;i++){
    Set set=new HashSet();//存每行元素
    Set set2=new HashSet();//存每列元素
    int p=0,q=0;
    for(int j=0;j<9;j++){
        if(board[i][j]!='.'){
            set.add(board[i][j]);
            p++;
        }
        if(set.size()!=p) return false;//每行 不能出现重复
        if(board[j][i]!='.'){
            set2.add(board[j][i]);
            q++;
        }
        if(set2.size()!=q) return false;//每列 不能出现重复
    }
}
for(int i=0;i<9;i+=3){
    for(int j=0;j<9;j+=3){//画成九个3*3的九宫
        Set set=new HashSet();
        int p=0;
        for(int a=i;a

方法二 简单易懂效率高法

// 记录某行,某位数字是否已经被摆放
boolean[][] row = new boolean[9][9];
// 记录某列,某位数字是否已经被摆放
boolean[][] col = new boolean[9][9];
// 记录某 3x3 宫格内,某位数字是否已经被摆放
boolean[][] block = new boolean[9][9];
for (int i = 0; i < 9; i++) {
    for (int j = 0; j < 9; j++) {//遍历每个元素
        if (board[i][j] != '.') {
            int num = board[i][j] - '1';//下标从0开始
            int blockIndex = i / 3 * 3 + j / 3;
            if (row[i][num] || col[j][num] || block[blockIndex][num]) {
                return false;
            } else {
                row[i][num] = true;
                col[j][num] = true;
                block[blockIndex][num] = true;
            }
        }
    }
}
return true;

方法三 官方解_一次迭代法

//遍历数独;检查看到每个单元格值是否已经在当前的行/列/子数独中出现过:
    //·如果出现重复,返回 false
    //·如果没有,则保留此值以进行进一步跟踪 
//返回true
// init data
HashMap [] rows = new HashMap[9];
HashMap [] columns = new HashMap[9];
HashMap [] boxes = new HashMap[9];
for (int i = 0; i < 9; i++) {
  rows[i] = new HashMap();
  columns[i] = new HashMap();
  boxes[i] = new HashMap();
}
// validate a board
for (int i = 0; i < 9; i++) {
  for (int j = 0; j < 9; j++) {
    char num = board[i][j];
    if (num != '.') {
      int n = (int)num;
      int box_index = (i / 3 ) * 3 + j / 3;
      // keep the current cell value
      rows[i].put(n, rows[i].getOrDefault(n, 0) + 1);//行
      columns[j].put(n, columns[j].getOrDefault(n, 0) + 1);//列
      boxes[box_index].put(n, boxes[box_index].getOrDefault(n, 0) + 1);//小九宫

      // check if this value has been already seen before
      if (rows[i].get(n) > 1 || columns[j].get(n) > 1 || boxes[box_index].get(n) > 1)
        return false;
    }
  }
}
return true;

1.11 旋转图像

给定一个 n × n 的二维矩阵表示一个图像。
将图像顺时针旋转 90 度。
说明:
你必须在原地旋转图像,这意味着你需要直接修改输入的二维矩阵。请不要使用另一个矩阵来旋转图像。

方法一 对角线翻转后水平镜像翻转=旋转

int n=matrix.length;
//翻转矩阵
for(int i=0;i

方法二 自外向内顺时针循环

int n=matrix.length;
int c=0;//循环层数
while(c

为帮助理解方法二附图:
LeetCode_初级算法_数组_第3张图片

你可能感兴趣的:(笔记,LeetCode,初级算法,数组)