2021-08-01

分类: 专题 3:数组 | 算法与数据结构入门教程  https://liweiwei1419.gitee.io/leetcode-algo/categories/%E4%B8%93%E9%A2%98-3%EF%BC%9A%E6%95%B0%E7%BB%84/

387 字符串中的第一个唯一字符 - 字符串中的第一个唯一字符 - 力扣(LeetCode)  https://leetcode-cn.com/problems/first-unique-character-in-a-string/solution/zi-fu-chuan-zhong-de-di-yi-ge-wei-yi-zi-x9rok/

方法一:使用哈希表存储频数
思路与算法

我们可以对字符串进行两次遍历。

在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回它的索引,否则在遍历结束后返回 -1−1。

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> frequency = new HashMap<Character, Integer>();
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            frequency.put(ch, frequency.getOrDefault(ch, 0) + 1); //默认值0.get是获得存储的次数 这次+1
        }
        for (int i = 0; i < s.length(); ++i) {
            if (frequency.get(s.charAt(i)) == 1) {  //遍历数组
                return i;
            }
        }
        return -1;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string/solution/zi-fu-chuan-zhong-de-di-yi-ge-wei-yi-zi-x9rok/
来源:力扣(LeetCode)

方法二:使用哈希表存储索引
思路与算法

我们可以对方法一进行修改,使得第二次遍历的对象从字符串变为哈希映射。

具体地,对于哈希映射中的每一个键值对,键表示一个字符,值表示它的首次出现的索引(如果该字符只出现一次)或者 -1−1(如果该字符出现多次)。当我们第一次遍历字符串时,设当前遍历到的字符为 cc,如果 cc 不在哈希映射中,我们就将 cc 与它的索引作为一个键值对加入哈希映射中,否则我们将 cc 在哈希映射中对应的值修改为 -1−1。

在第一次遍历结束后,我们只需要再遍历一次哈希映射中的所有值,找出其中不为 -1−1 的最小值,即为第一个不重复字符的索引。如果哈希映射中的所有值均为 -1−1,我们就返回 -1−1。

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string/solution/zi-fu-chuan-zhong-de-di-yi-ge-wei-yi-zi-x9rok/
来源:力扣(LeetCode)

class Solution {
    public int firstUniqChar(String s) {
        Map<Character, Integer> position = new HashMap<Character, Integer>();
        int n = s.length();
        for (int i = 0; i < n; ++i) {
            char ch = s.charAt(i);
            if (position.containsKey(ch)) {
                position.put(ch, -1);
            } else {
                position.put(ch, i); //方法二存的下标i
            }
        }
        int first = n; //记录
        for (Map.Entry<Character, Integer> entry : position.entrySet()) {
            int pos = entry.getValue();
            if (pos != -1 && pos < first) {
                first = pos;
            }
        }
        if (first == n) { //最后比较
            first = -1;
        }
        return first;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/first-unique-character-in-a-string/solution/zi-fu-chuan-zhong-de-di-yi-ge-wei-yi-zi-x9rok/
来源:力扣(LeetCode)

队列思路?

遍历map for( :)?

215 数组中的第K个最大元素 - 数组中的第K个最大元素 - 力扣(LeetCode)  https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/shu-zu-zhong-de-di-kge-zui-da-yuan-su-by-leetcode-/

堆排序 建立堆 各种操作?

我们也可以使用堆排序来解决这个问题——建立一个大根堆,做 k - 1k−1 次删除操作后堆顶元素就是我们要找的答案。在很多语言中,都有优先队列或者堆的的容器可以直接使用,但是在面试中,面试官更倾向于让更面试者自己实现一个堆。所以建议读者掌握这里大根堆的实现方法,在这道题中尤其要搞懂「建堆」、「调整」和「删除」的过程。

友情提醒:「堆排」在很多大公司的面试中都很常见,不了解的同学建议参考《算法导论》或者大家的数据结构教材,一定要学会这个知识点哦!^_^

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/shu-zu-zhong-de-di-kge-zui-da-yuan-su-by-leetcode-/
来源:力扣(LeetCode)
???????????

class Solution {?????
    public int findKthLargest(int[] nums, int k) {
        int heapSize = nums.length;
        buildMaxHeap(nums, heapSize);
        for (int i = nums.length - 1; i >= nums.length - k + 1; --i) {
            swap(nums, 0, i);
            --heapSize;
            maxHeapify(nums, 0, heapSize);
        }
        return nums[0];
    }

    public void buildMaxHeap(int[] a, int heapSize) {
        for (int i = heapSize / 2; i >= 0; --i) {
            maxHeapify(a, i, heapSize);
        } 
    }

    public void maxHeapify(int[] a, int i, int heapSize) {
        int l = i * 2 + 1, r = i * 2 + 2, largest = i;
        if (l < heapSize && a[l] > a[largest]) {
            largest = l;
        } 
        if (r < heapSize && a[r] > a[largest]) {
            largest = r;
        }
        if (largest != i) {
            swap(a, i, largest);
            maxHeapify(a, largest, heapSize);
        }
    }

    public void swap(int[] a, int i, int j) {
        int temp = a[i];
        a[i] = a[j];
        a[j] = temp;
    }
}

作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/solution/shu-zu-zhong-de-di-kge-zui-da-yuan-su-by-leetcode-/
来源:力扣(LeetCode)

快速排序具体代码?

136 一秒就能搞懂的动图解析 - 只出现一次的数字 - 力扣(LeetCode)  https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/

有6种方法和容易懂的动图,(新)第6种异或

方法一 HashMap
解析
用 HashMap 的这个方法是很容易实现的,题目要求不是让我们求次数嘛,那我们直接遍历数组将每个数字和其出现的次数存到 哈希表里 就可以了,然后我们再从哈希表里找出出现一次的那个数返回即可。

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int singleNumber(int[] nums) {
        //特殊情况
        if (nums.length == 1) {
            return nums[0];
        }
        //HashMap
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        //将其存入哈希表中,含义为,若该元素不存在则存入表中,并计数为1,若已经存在获取次数并加1.
        for (int x : nums) {
            map.put(x , map.getOrDefault(x,0) + 1);
        }
        //遍历出出现次数为1的情况
        for (int y : map.keySet()) { //和第一题不一样,遍历key写法是map.keySet()
            if(map.get(y) == 1){
                return y;
            }
        }    
        return 0;

    }
}

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)

方法二

排序搜索法
解析
这个方法也是特别容易想到的,我们首先对数组进行排序,然后遍历数组,因为数组中其他数字都出现两次,只有目标值出现一次,所以则让我们的指针每次跳两步,当发现当前值和前一位不一样的情况时,返回前一位即可,当然我们需要考虑这种情况,当我们的目标值出现在数组最后一位的情况,所以当数组遍历结束后没有返回值,则我们需要返回数组最后一位

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int singleNumber(int[] nums) {
        if (nums.length == 1){ //别忘了判断
            return nums[0];
        }
        //排序
        Arrays.sort(nums); //排序?直接?
        for (int i = 1; i < nums.length-1; i+=2){ //从1 开始,指针指向相同两个的后一个
            if (nums[i] == nums[i-1]){            //因为和i-1比较
                continue;          //continue
            }else{
                return nums[i-1];
            }
        }
        return nums[nums.length-1]; //最后一个

    }
}

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)

三 

HashSet
解析
这个方法也是比较容易实现的,我们利用 HashSet 来完成。HashSet 在我们刷题时出现频率是特别高的,它是基于 HashMap 来实现的,是一个不允许有重复元素的集合。那么在这个题解中,它起到什么作用呢?解题思路如下,我们依次遍历元素并与 HashSet 内的元素进行比较,如果 HashSet 内没有该元素(说明该元素第一次出现)则存入,若是 HashSet 已经存在该元素(第二次出现),则将其从 HashSet 中去除,并继续遍历下一个元素。最后 HashSet 内剩下的则为我们的目标数。思路和我们之前说过的括号匹配问题类似,

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int singleNumber(int[] nums) {
         if (nums.length == 1){
             return nums[0];
         }
         HashSet<Integer> set = new HashSet<>(); //hashset创建
         //循环遍历
         for (int x : nums){
             //已经存在,则去除
             if(set.contains(x)){  //判断包含的写法
                 set.remove(x);    //移除
             }
             //否则存入
             else{
                 set.add(x);   //添加
             }
         }
         //返回仅剩的一个元素
         return set.iterator().next();  //???
    }
}

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)

四 栈
解析
该方法也很容易想到,我们首先将其排序,然后遍历数组,如果栈为空则将当前元素压入栈,如果栈不为空,若当前元素和栈顶元素相同则出栈,继续遍历下一元素,如果当前元素和栈顶元素不同的话,则说明栈顶元素是只出现一次的元素,我们将其返回即可。这个题目也可以使用队列做,思路一致,

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

class Solution {
    public int singleNumber(int[] nums) {
        if (nums.length == 1) {
            return nums[0];
        }
        Arrays.sort(nums);
        Stack<Integer> stack = new Stack<>();
        for (int x : nums){
           if (stack.isEmpty()) {
               stack.push(x);
               continue;
           }
            //不同时直接跳出
           if (stack.peek() != x) {
               break;
           }
            //相同时出栈
           stack.pop();           
        }
        return stack.peek();
    }
}

作者:yuan-chu-de-suan-fa-xiao-wu
链接:https://leetcode-cn.com/problems/single-number/solution/dong-hua-dong-tu-yi-ding-hui-by-yuan-chu-vs4p/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

五 求和法
解析
这个方法也比较简单,也是借助咱们的 HashSet ,具体思路如下,我们通过 HashSet 保存数组内的元素,然后进行求和(setsum),那么得到的这个和则为去除掉重复元素的和,我们也可以得到所有元素和(numsum)。因为我们其他元素都出现两次,仅有一个元素出现一次,那我们通过 setsum * 2 - numsum 得到的元素则为出现一次的数。

作者:yuan-chu-de-suan-fa-xiao-wu

class Solution {
    public int singleNumber(int[] nums) {
       if (nums.length == 1){
           return nums[0];
       }
       HashSet<Integer> set = new HashSet<>();
       int setsum = 0;
       int numsum = 0;
       for (int x : nums) {
           //所有元素的和
           numsum += x; 
           if (!set.contains(x)) {
               //HashSet内元素的和
               setsum += x; 
           }
           set.add(x);
       } 
       //返回值
       return setsum * 2 - numsum;
    }
}

作者:yuan-chu-de-suan-fa-xiao-wu

六 位运算

只出现一次的数Ⅱ

给定一个非空整数数组,除了某个元素只出现一次以外,其余每个元素均出现了三次。找出那个只出现了一次的元素。

求和法和位运算

只出现一次的数Ⅲ
给定一个整数数组 nums,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。

示例 :

输入: [1,2,1,3,2,5]
输出: [3,5]

这个也很容易理解,算是对第一题的升级,第一题有 1 个出现 1次的数,其余出现两次,这个题目中有 2 个出现 1次的数,其余数字出现两次。那么这个题目我们怎么做呢?我们看一下能不能利用第一题中的做法解决。

HashSet
解析
这个做法和我们第一题的做法一致,只要理解了第一题的做法,这个很容易就能写出来,有一点不同的是,第一题的 HashSet 里面最后保留了一个元素,该题保留两个元素。

作者:yuan-chu-de-suan-fa-xiao-wu

class Solution {
    public int[] singleNumber(int[] nums) {
        HashSet<Integer> set = new HashSet<Integer>();
        for (int x : nums) {
            //存在的则移除
            if (set.contains(x)) {
                set.remove(x);
                continue;
            }
            //不存在存入
            set.add(x);
        }
        //存到数组里,然后返回
        int[] arr = new int[2];
        int i = 0;
        for (int y : set) {
           arr[i++] = y; 
        }
        return arr;
    }
}

作者:yuan-chu-de-suan-fa-xiao-wu

位运算

最后有作者github地址 动图

github总 https://github.com/chefyuan/algorithm-base/tree/main/animation-simulation

github数组https://github.com/chefyuan/algorithm-base/tree/main/animation-simulation/%E6%95%B0%E7%BB%84%E7%AF%87

1两数之和 https://github.com/chefyuan/algorithm-base/blob/main/animation-simulation/%E6%95%B0%E7%BB%84%E7%AF%87/leetcode1%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C.md

题目描述:

给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。

示例:

给定 nums = [2, 7, 11, 15], target = 9

因为 nums[0] + nums[1] = 2 + 7 = 9 所以返回 [0, 1]

双指针(暴力)法

双指针(L,R)法的思路很简单,L 指针用来指向第一个值,R 指针用来从第 L 指针的后面查找数组中是否含有和 L 指针指向值和为目标值的数。

例:绿指针指向的值为 3,蓝指针需要在绿指针的后面遍历查找是否含有 target - 3 = 2 的元素,若含有返回即可。
class Solution {
    public int[] twoSum(int[] nums, int target) {
        if(nums.length < 2){
            return new int[0];
        }
        int[] rearr = new int[2]; //先定义了保存结果的数组
        //查询元素
        for(int i = 0; i < nums.length; i++){
            for(int j = i+1; j < nums.length; j++ ){
                //发现符合条件情况
                if(nums[i] + nums[j] ==target){
                    rearr[0] = i;
                    rearr[1] = j;  
 //return写到这里会不会好?避免得到结果重复循环
                }
            }  
        }
        return rearr;
    }
}
哈希表
哈希表的做法很容易理解,我们只需通过一次循环即可,假如我们的 target 值为 9,当前指针指向的值为 2 ,我们只需从哈希表中查找是否含有 7,因为 9 - 2 =7 。如果含有 7 我们直接返回即可,如果不含有则将当前的 2 存入哈希表中,指针移动,指向下一元素。注: key 为元素值,value 为元素索引。
返回数组的写法 参数数组的写法 public int[] twoSum(int[] nums, int target) {}
class Solution {
    public int[] twoSum(int[] nums, int target) {
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i = 0; i < nums.length; i++){
            //如果存在则返回
            if(map.containsKey(target-nums[i])){
                return new int[]{map.get(target-nums[i]),i}; //直接返回新建的数组
            }
            //不存在则存入
            map.put(nums[i],i);  //存i有什么用?

        }
        return new int[0];

    }
}

27 移除元素https://github.com/chefyuan/algorithm-base/blob/main/animation-simulation/%E6%95%B0%E7%BB%84%E7%AF%87/leetcode27%E7%A7%BB%E9%99%A4%E5%85%83%E7%B4%A0.md

给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。

不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。

元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。

示例 1:

给定 nums = [3,2,2,3], val = 3,

函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。

你不需要考虑数组中超出新长度后面的元素。

暴力法  题目含义就是让我们删除掉数组中的元素,然后将数组后面的元素跟上来。最后返回删除掉元素的数组长度即可。比如数组长度为 10,里面有 2 个目标值,我们最后返回的长度为 8,但是返回的 8 个元素,需要排在数组的最前面。那么暴力解法的话则就需要两个 for 循环,一个用来找到删除,另一个用来更新数组。

class Solution {
    public int removeElement(int[] nums, int val) {
        //获取数组长度
        int len = nums.length;
        if (len == 0) {
            return 0;
        }
        int i = 0;
        for (i = 0; i < len; ++i) { //len后面会-1,不能写固定的nums.length
            //发现符合条件的情况
            if (nums[i] == val) {
                //前移一位
                for (int j = i; j < len-1; ++j) {
                    nums[j] = nums[j+1];
                }
                i--;  //注意-1
                len--;
            }
        }
        return i;
    }
}

为什么i--??(2)我们每找到一个需要删除的值的时候,需要 i--,防止出现多个需要删除的值在一起的情况,然后漏删。???

(新)解法二双指针 后面蓝色是前指针。!用if continue跳过了if后面的后指针+1和替换的步骤

快慢指针的做法比较有趣,只需要一个 for 循环即可解决,时间复杂度为 O(n) ,总体思路就是有两个指针,前面一个后面一个,前面的用于搜索需要删除的值,当遇到需要删除的值时,前指针直接跳过,后面的指针不动,当遇到正常值时,两个指针都进行移动,并修改慢指针的值。最后只需输出慢指针的索引即可。

class Solution {
    public int removeElement(int[] nums, int val) {
          int len = nums.length;
          if (len == 0) { //先判断,不判断好像也是返回0?
              return 0;
          }
          int i = 0;
          for (int j = 0; j < len; ++j) {
                //如果等于目标值,则删除
                if (nums[j] == val) {
                    continue;
                }
                // 不等于目标值时,则赋值给nums[i],i++
                nums[i++] = nums[j]; //+1和赋值
          }
          return i;
    }
}

扁扁的瘫22:49

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值