分类: 专题 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
题目描述:
给定一个整数数组 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];
}
}
给你一个数组 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