数据结构之(一):数组

数组初体验之数组中重复的数字
数组 : 有限个 相同类型的变量 组成的有序集合
int[] arr;
int arr[];
// 静态初始化
String[] strArr = {" 和平精英 "," 王者荣耀 "," 开心消消乐 "," 欢乐斗地主 "};
// 动态初始化
String[] strArr1 = new String[4];

 

数组中重复的数字
https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/

 数组中重复的数字

找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0 n-1 的范围内。数组中某些数字是重复的,
但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数
字。
示例 1
输入:
[2, 3, 1, 0, 2, 5, 3]
输出: 2 3
解法
1) 集合的解法 set
使用集合存储已遍历过的数据,新数据遍历时,判断是否在集合中存在,如果存在即为重复的
   public static int findRepeatNumber(int[] nums) {
        Set<Integer> set = new HashSet<>();
// 遍历数组 iter快捷键
        for (int num : nums) {
            if (set.contains(num)) {
                System.out.println("发现重复元素" + num);
                return num;
            }
            System.out.println("添加元素" + num);
            set.add(num);
        }
        return -1;
    }
2) 先排序后查找
排序后重复元素相邻
[2, 3, 1, 0, 2, 5, 3]
[0, 1, 2, 2, 3, 3, 5]
   public static int findRepeatNumber1(int[] nums) {
// 操作数组的工具类 进行排序
        System.out.println(Arrays.toString(nums));
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));
// itar快捷键 通过判断相邻元素是否相等 返回是否重复
        for (int i = 1; i < nums.length; i++) {
            if (nums[i] == nums[i - 1]) {
                return nums[i];
            }
        }
        return -1;
    }
3) 临时数组
因为长度为 n 的数组 nums 里的所有数字都在 0 n-1
所以 临时数组中的索引 对应所有可能出现的数字
遍历时 将出现数字 对应到临时数组的索引位置 更改元素值 0 - 1
当临时数组的元素 不为 0 说明此索引位置 已经出现过元素了
[2, 3, 1, 0, 2, 5, 3]
[0, 0, 0, 0, 0, 0, 0]
0 1 2 3 4 5 6
[0, 0, 1, 0, 0, 0, 0]
[0, 0, 1, 1, 0, 0, 0]
[0, 1, 1, 1, 0, 0, 0]
[1, 1, 1, 1, 0, 0, 0]
    public static int findRepeatNumber2(int[] nums) {
        System.out.println(Arrays.toString(nums));
// 临时数组 所有元素默认是0
        int[] tmp = new int[nums.length];
        System.out.println(Arrays.toString(tmp));
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
// 如果已经复制 代表重复出现
            if (tmp[num] != 0) {
                return num;
            }
// 找到临时数组的索引位置 赋值为1
            tmp[num] = 1;
            System.out.println(Arrays.toString(tmp));
        }
        return -1;
    }
4) 交换位置查找
[2, 3, 1, 0, 2, 5, 3]
0 1 2 3 4 5 6
[0, 1, 2, 3, 2, 5, 3]
0 1 2 3 4 5 6
[2, 3, 1, 0, 2, 5, 3]
遍历数组的过程中
希望当前位置 和 出现元素正好匹配上
先判断 是否匹配 如果不匹配 进行交换 并且看需要交换的位置 是否存在期望元素
如果可以交换 交换之后 继续遍历当前位置 如果不可交换 即为重复元素
0 1 2 3 4 5 6
遍历 2 交换 2 1 [1, 3, 2, 0, 2, 5, 3]
遍历 1 交换 1 3 [3, 1, 2, 0, 2, 5, 3]
遍历 3 交换 3 0 [0, 1, 2, 3, 2, 5, 3]
遍历 0 不交换
遍历 1 不交换
遍历 2 不交换
遍历 3 不交换
遍历 2 已经出现期望元素 是重复元素
    public static int findRepeatNumber3(int[] nums) {
        System.out.println(Arrays.toString(nums));
// [2, 3, 1, 0, 2, 5, 3]
        for (int i = 0; i < nums.length; i++) {
// 如果索引正好等于元素本身 是期望的结果 跳过
            if (nums[i] == i) continue;
// i = 0
            int num = nums[i]; // 2
// 如果要交换的位置 已经有期望值 说明重复
            if (nums[num] == num) {
                System.out.println("当前索引为" + num + "的位置 已经有" + num +
                        "值,重复了");
                return num;
            }
            int tmp = nums[num]; // 1
            nums[num] = num;
            nums[i] = tmp;
// [1, 3, 2, 0, 2, 5, 3]
// 交换仍需遍历当前位置的值 所以抵消i++
            i--;
            System.out.println(Arrays.toString(nums));
        }
        return -1;
    }
数组初体验之数组中消失的数字
数组中消失的数字
https://leetcode-cn.com/problems/fifind-all-numbers-disappeared-in-an-array/
找到所有数组中消失的数字
给定一个范围在 1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一
些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为 O(n) 的情况下完成这个任务吗 ? 你可以假定返回的数组不算
在额外空间内。
示例 :
输入 :
[4,3,2,7,8,2,3,1]
输出 :
[5,6]
解法:
1 )使用容器存储已出现的数字
[4,3,2,7,8,2,3,1]
额外容器 [4,3,2,7,8,1]
1-8( 数组的长度 ) [5,6]
set list map
不重复无序 适用 set
可能重复且有序 适用 list
映射关系 < 元素,出现的次数 > map
    public static List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> result = new ArrayList<>();
// 使用set 存储已出现的数字
        Set<Integer> set = new HashSet<>();
        for (int num : nums) {
            set.add(num);
        }
// 从1-n开始遍历
        for (int i = 1; i <= nums.length; i++) {
            if (!set.contains(i)) {
                result.add(i);
            }
        }
        return result;
    }
2 )不使用额外的空间?
[1,2,4,5,1] 1-5 [3]
[1,2,4,5,1]
0 1 2 3 4
1 2 3 4 5
能不能利用 索引 来记录已出现的数字?
元素 索引 i i 位的值进行变化 说明
1 0 [-1,2,4,5,1] 1 变为 -1
2 1 [-1,-2,4,5,1] 2 变为 -2
4 3 [-1,-2,4,-5,1] 5 变为 -5
5 4 [-1,-2,4,-5,-1] 倒数的 1 变为 -1
1 0 [-1,-2,4,-5,-1]
剩余还是正数的元素 索引对应的值从未出现过
    public static List<Integer> findDisappearedNumbers1(int[] nums) {
        List<Integer> result = new ArrayList<>();
// 元素对应的索引 将指定位置元素 置为负数
        for (int i = 0; i < nums.length; i++) {
            int num = Math.abs(nums[i]);
            int index = num - 1;
// 将对应索引位置的值置为负数 前提是这个数是整数
            if (nums[index] > 0) {
                nums[index] = -nums[index];
                System.out.println("索引" + index + " " + Arrays.toString(nums));
            }
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) {
                result.add(i + 1);
                System.out.println(nums[i] + "元素仍为正数,找到" + (i + 1));
            }
        }
        return result;
    }
数组初体验之多数元素
多数元素
https://leetcode-cn.com/problems/majority-element/
给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 n/2
元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入 : [3,2,3]
输出 : 3
示例 2:
输入 : [2,2,1,1,1,2,2]
输出 : 2
解法分析:
1 )使用 map 记录 < 元素,出现次数 >
如果次数超过 n/2 即为要查找的元素
数组 [3,2,3]
map <3,1> <2,1> <3,2>
判断条件 2 > 3/2 = 1
    public static int majorityElement(int[] nums) {
// map 记录元素出现个数
        Map<Integer, Integer> map = new HashMap<>();
// 统计 多数元素 满足结果
        int halfLen = nums.length / 2;
        for (int i = 0; i < nums.length; i++) {
            int num = nums[i];
// 元素次数 初始化是1
            int count = 1;
            if (map.containsKey(num)) {
                count = map.get(num);
                count++;
            }
// 判断是否是多数元素
            if (count > halfLen) {
                return num;
            }
            System.out.println(num + "元素,存储次数为" + count);
            map.put(num, count);
        }
        return -1;
    }
2 )排序后取中间元素
[3,2,3]
[2,3,3]
[2,2,1,1,1,2,2]
[1,1,1,2,2,2,2]
[1,1,1,2]
[1,2,2,2]
    public static int majorityElement1(int[] nums) {
        Arrays.sort(nums);
        System.out.println(Arrays.toString(nums));
        int halfLen = nums.length / 2;
        return nums[halfLen];
    }
3 )投票算法
将元素当做候选人,如果新元素和候选人相同,次数 +1, 不同次数 -1( 代表抵消 )
如果次数 =0 ,候选人更新
[2,2,1,1,1,2,2]
元素 次数 候选人
2 1 2
2 2
1 1
1 0
1 1 1
2 0
2 1 2
本质上 多数元素出现的次数 一定大于其他所有元素出现次数的总和 所以候选人的次数经过抵消后
最终剩余的一定是 多数元素
    public static int majorityElement2(int[] nums) {
// 候选人
        int candidate = -1;
// 出现次数
        int count = 0;
        for (int i = 0; i < nums.length; i++) {
// 如果出现次数为0 重置候选人
            if (count == 0) {
                candidate = nums[i];
            }
// 根据新元素是否等于候选人 进行次数的加减操作
            if (candidate == nums[i]) {
                count++;
            } else {
                count--;
            }
            System.out.println("元素" + nums[i] + ",次数" + count
                    + ",候选人" + candidate);
        }
        return candidate;
    }
稀疏数组的定义和转化
稀疏数组
二维数组 : 每个元素都是一维数组
int[][] arr = new int[10][10]
0 1 2 3 4 5 6 7 8 9
0 [0,0,0,0,0,0,0,0,0,0]
1 [0,0,1,0,0,0,0,0,0,0]
2 [0,0,2,0,0,0,0,0,0,0]
3 [0,2,0,0,0,0,0,0,0,0]
4 [0,0,0,0,0,0,0,0,0,0]
5 [0,0,0,0,0,0,0,0,0,0]
6 [0,0,0,0,0,0,0,0,0,0]
7 [0,0,0,0,0,0,0,0,0,0]
8 [0,0,0,0,0,0,0,0,0,0]
9 [0,0,0,0,0,0,0,0,0,0]
稀疏数组
保存原始的行数和列数
保存非零元素的位置
元素值
10 10 3
1 2 1
3 1 2
2 2 2
(n+1)*3 列的数组 压缩, n 代表非零元素的个数
第一行 存储总行数 总列数 和 非零元素的个数
接下来每一行 存储 元素的行数 列数 和 自身元素值
    // 把普通数组 转化为 稀疏数组
    public static int[][] toSparse(int[][] arr) {
// (n+1)*3列的数组 初始化
// 需要先求 非零元素的个数
        int count = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (arr[i][j] != 0) {
                    count++;
                }
            }
        }
        int[][] result = new int[count + 1][3];
        result[0][0] = arr.length;
        result[0][1] = arr[0].length;
        result[0][2] = count;
// 遍历数组 找到所有非零元素 存储到结果中
        int index = 0;
        for (int i = 0; i < arr.length; i++) {
            for (int j = 0; j < arr.length; j++) {
                if (arr[i][j] != 0) {
                    index++;
                    result[index][0] = i;
                    result[index][1] = j;
                    result[index][2] = arr[i][j];
                }
            }
        }
        return result;
    }
经典算法思想之双指针

双指针
索引 i 是一个指针 一个引用
for(int i=0;i<arr.length;i++){
int num = arr[i];
}
两个指针 —— 双指针
如果两个指针是相反方向,叫对撞指针
如果两个指针是相同方向,通过速度的不同,或者起点的不同,达到处理的目的,叫快慢指针
删除排序数组中的重复项
https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/
给定一个排序数组,你需要在 原地 删除重复出现的元素,使得每个元素只出现一次,返回移除后
数组的新长度。
不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。 示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4
你不需要考虑数组中超出新长度后面的元素
分析:
需要额外指针 记录新出现的不同元素 需要覆盖的位置

    public static int removeDuplicates(int[] nums) {
        if (nums.length == 0) return 0;
// 记录要比较的元素位置
        int index = 0;
        for (int i = 1; i < nums.length; i++) {
// 如果不同 进行覆盖
            if (nums[i] != nums[index]) {
                index++;
                nums[index] = nums[i];
                System.out.println("覆盖第" + index + "位置的元素为" + nums[i]);
            }
        }
// 新数组的长度 = 最大索引+1
        return index + 1;
    }
双指针应用之移除元素
移除元素
https://leetcode-cn.com/problems/remove-element/

给一个数组 nums 和一个值 val ,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数
组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2,
函数应该返回新的长度 5, 并且 nums 中的前五个元素为 0, 1, 3, 0, 4
注意这五个元素可为任意顺序。
你不需要考虑数组中超出新长度后面的元素。
分析:
解法 1 ) 双指针覆盖
[3,2,1,3] val = 3
使用额外指针 记录要覆盖的索引位置
索引
3 0 [3,2,1,3]
2 1 [2,2,1,3]
1 2 [2,1,1,3]
3
[0,1,2,2,3,0,4,2] val = 2
索引初始值是 0
元素 索引 数组
0 1 [0,1,2,2,3,0,4,2]
1 2 [0,1,2,2,3,0,4,2]
2
2
3 3 [0,1,3,2,3,0,4,2] 0 4 [0,1,3,0,3,0,4,2]
4 5 [0,1,3,0,4,0,4,2]
2
    public static int removeElement(int[] nums, int val) {
// 记录要覆盖的索引位置
        int index = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != val) {
// 覆盖的是当前位置
                nums[index] = nums[i];
                System.out.println("将" + nums[i] + "覆盖到" + index + "位置上");
                index++;
            }
        }
// 新数组的长度大小
        return index;
    }
解法 2 )直接替换
[0,1,2,2,3,0,4,2] val=2
[0,1,2,2,3,0,4|,2] len=7
[0,1,4,2,3,0|,2,2] len=6
[0,1,4,0,3|,2,2,2] len=5
index=0 len=8
判断当前值 是否需要被删除
如果需要 进行替换 替换最后一个位置的元素
nums[index] = nums[len-1]
len--;
index--; // 替换回来的值 仍要进行判断
index++
index >= len 循环结束
    public static int removeElement1(int[] nums, int val) {
        int index = 0;
// 记录有效元素的个数
        int len = nums.length;
        while (true) {
            if (index >= len) return len;
            if (nums[index] == val) {
                nums[index] = nums[len - 1];
                nums[len - 1] = val;
                System.out.println("替换位置" + index + "和" + (len - 1) + "的元素");
                        System.out.println(Arrays.toString(nums));
                len--;
                index--;
            }
            index++;
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值