给定一个整数数组 nums
,其中恰好有两个元素只出现一次,其余所有元素均出现两次。 找出只出现一次的那两个元素。你可以按 任意顺序 返回答案
思路: x&(-x)
取到的结果就是把自身x最低位1和后面的0保存,其余位都是0
e.g:
lowbit(6):6的二进制是(110)所以结果就是(10)
lowbit(7):7的二进制是(111)所以结果就是(1)
然后把结果转化10进制就可以了
快速办法直接看 最低位1取到的是第几位,然后2的几次方就可以了
比如lowbit(6) 最低位1 在第1位上 所以2^1=2;
lowbit (7) 最低位1 在第0位上 所以2^0=1
用途: 一般可以用来获取某个二进制数的LowBit
然后分别与各个数做&运算,&结果为0的为一组,结果不为0的为1组 分别做^运算得到两个数x1 x2
题解:
class Solution {
public int[] singleNumber(int[] nums) {
//总体思路:把不同的两个数放在两个小组
//如何分组?
//由异或的性质 a^a = 0, a^0 = a
//先把所有数值异或 得到结果x = x1^x2
//再由因为x1!= x2 ,一定有一位为1, x&(-x)得到最低位比特为1的值 将这一位作为分组依据
int x = 0;
for(int e:nums){
x=e^x;
}//得到所有元素的异或和x
int ground = x&(-x); //6的二进制是(0110)所以结果就是(0010)
int x1 = 0;
int x2 = 0;
for(int e : nums){
if((e & ground) == 0){ //位为0
x1 =x1^ e;
}else{
x2 =x2^ e;
}
}
return new int[]{x1,x2};
}
}
整数转换。编写一个函数,确定需要改变几个位才能将整数A转成整数B
思路:对两个整数异或 判断有几个1。
or&(or -1) //消去or二进制表示的最低位的1
class Solution {
public int convertInteger(int A, int B) {
int or = A^B;
//将问题转换为or中1的个数 因为不同位为1
int cnt = 0;
while(or != 0){
or = or&(or -1);
cnt++;
}
return cnt;
}
}
思路:二分查找细节详解
class Solution {
public int search(int[] nums, int target) {
int low = 0;
int high = nums.length - 1;
int mid = 0;
//假设target在[left right]中 显然结束循环的条件如果是
while(low <= high){
mid = (low + high)/2;
if(nums[mid] > target){
high = mid - 1;
}else if(nums[mid] < target){
low = mid + 1;
}else{
return mid;
}
}
return -1;
}
}
给你一个按 非递减顺序 排序的整数数组 nums
,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
思路:因为数组是一个递增的顺序 平方的最大值一定是在数组的两边。所以考虑从两个向中间遍历不断找平方的最大值。注意有负数。
class Solution {
public int[] sortedSquares(int[] nums) {
//最大值一定在两侧取到
int n = nums.length;
int []res = new int[n];
int left = 0,right = n - 1,k = n - 1;
while(left <= right && k >= 0){
if(Math.abs(nums[left]) < Math.abs(nums[right])){
res[k--] = nums[right] * nums[right];
right--;
}else{
res[k--] = nums[left] * nums[left];
left++;
}
}
return res;
}
}
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0
思路:滑动窗口
class Solution {
public int minSubArrayLen(int target, int[] nums) {
//left right表示一个窗口
int left = 0;
int right = 0;
int minLen =Integer.MAX_VALUE;
int sum = 0;
while(right < nums.length){
sum += nums[right];
//寻找满足条件的最小左边界
while(sum >= target){
//此时满足条件 记录此时的长度 并与最小长度比较
minLen = Math.min(minLen,right - left +1);
//缩减窗口 找最短的长度
sum -= nums[left];
left++;
}
//此时的sum一定
right++;
}
//若没有连续大于target的子数组 minLen不发生变化 则返回0
return minLen == Integer.MAX_VALUE ? 0: minLen;
}
}
给定一个字符串 s
,请你找出其中不含有重复字符的 最长子串 的长度。
思路:
找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。
用左指针指向每个开始的子字符 右指针不断向后遍历 若出现了重复元素记录之前的长度,直到出现重复元素为止。结束循环
因为右指针停止循环的位置之前都没有出现重复元素,故下次直接从right开始遍历即可
左指针向前移动一个单位,(注意:此时若不是第一个元素 需要把上一个的元素从set中移除)
改进思路(待实现):因为循环做了许多无效的循环 发现有重复字符时,可以直接把左指针移动到第一个重复字符的下一个位置即可。
class Solution {
public int lengthOfLongestSubstring(String s) {
if(s == null||s==""){
return 0;
}
if(s.length() == 1){
return 1;
}
int left = 0;
int right = 0;
int maxLen = 0;
Set<Character> set = new HashSet<>();
char []chars = s.toCharArray();
int len = chars.length;
for(;left < len ; left++){
//将上一个left的字符移除
if(left != 0){
set.remove(chars[left - 1]);
}
//到达right之前的元素一定是没有重复元素的 所以right无需移动
while(right < len && !set.contains(chars[right])){
set.add(chars[right]);
right++;
}
maxLen = Math.max(maxLen,right - left);
}
return maxLen;
}
}
Given a positive integer n, generate an n x n matrix filled with elements from 1 to n2 in spiral order.
思路:循环模拟 参考–Spiral Matrix II (模拟法,设定边界,代码简短清晰) - 螺旋矩阵 II
class Solution {
public int[][] generateMatrix(int n) {
//定义上下左右边界值
int [][] matrix = new int[n][n];
int top = 0;
int bottom = n - 1;
int left = 0;
int right = n -1;
int num = 1;
int square = n * n;
while(num <= square){
//从左到右填充 [left,right]
for(int i = left;i <= right;i++){
matrix[top][i] = num;
num++;
}
//对拐角处进行处理 也可以理解为缩小边界
top++;
//右边界从上到下填充
for(int i = top;i <= bottom;i++){
matrix[i][right] = num;
num++;
}
right--;
//下边界从右到左填充
for(int i = right;i >= left;i--){
matrix[bottom][i] = num;
num++;
}
bottom--;
//左边界从下到上填充
for(int i = bottom;i >= top;i--){
matrix[i][left] =num;
num++;
}
left++;
}
return matrix;
}
}
Hercy wants to save money for his first car. He puts money in the Leetcode bank every day.
He starts by putting in $1 on Monday, the first day. Every day from Tuesday to Sunday, he will put in $1 more than the day before. On every subsequent Monday, he will put in $1 more than the previous Monday.(比前一个周一存的钱数多1)
Given n, return the total amount of money he will have in the Leetcode bank at the end of the nth day.
思路:理解题意,等差数列求和问题,重温等差数列求和公式。。
class Solution {
//等差数列公式: an = a1 + (n-1) * d
//等差数列求和公式: Sn = (a1 + an) * n / 2
//显然每一个完整的周会比上一个完整的周多存7 设第一个完整的周为a1 则有an= a1 + (n-1) * 7
public int totalMoney(int n) {
int weekNum = n / 7; //有几个完整的一周
int dayNum = n % 7; //不完整的周有几天
int a1 = (1+7)*7/2; //第一个完整的周存的钱
int an = a1 +(weekNum-1)*7; //第weekNum个完整的周存的钱
int s1 = (a1+an)*weekNum /2;
//还有不完整的周所存钱数
//显然 不完整周的周存的钱为 weekNum+1
//而接下来的每一天存的钱比前一天多1 公差为1 的等差数列求和问题
int b1 = weekNum +1;
int bn = b1 + (dayNum-1)*1;
int s2 = (b1+bn)*dayNum / 2;
return s1 + s2;
}
}
public class Solution {
public ListNode detectCycle(ListNode head) {
//先判断有无环
if(head == null || head.next ==null){
return null;
}
ListNode slow , fast;
slow = fast = head;
while(fast.next!=null && fast.next.next!=null){
slow = slow.next;
fast = fast.next.next;
if(fast == slow){
//有环
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
return null;
}
}
思路:简单哈希
class Solution {
public boolean isAnagram(String s, String t) {
int [] lettersNum = new int[26];
char [] c1 = s.toCharArray();
char [] c2 = t.toCharArray();
for(int i = 0;i < s.length();i++){
char c = c1[i];
lettersNum[ c -'a']++;
}
for(int i = 0;i < t.length();i++){
char c =c2[i];
lettersNum[ c -'a']--;
}
for(int k = 0;k < 26;k++){
if(lettersNum[k] != 0){
return false;
}
}
return true;
}
}
思路:利用set集合的不存在重复元素的特性找交集
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> num1Set = new HashSet<Integer>();
Set<Integer> num2Set = new HashSet<Integer>();
for(int i:nums1){
num1Set.add(i);
}
for(int i:nums2){
num2Set.add(i);
}
return subSection(num1Set,num2Set);
}
public int[] subSection(Set<Integer> set1,Set<Integer> set2){
Set<Integer> minSet;
Set<Integer> bigSet;
Set<Integer> intersectionSet = new HashSet<Integer>();
int len = 0;
if(set1.size() > set2.size()){
minSet = set2;
bigSet = set1;
}else{
minSet = set1;
bigSet = set2;
}
for(int i:minSet){
if(bigSet.contains(i)){
intersectionSet.add(i);
}
}
int[] intersection = new int[intersectionSet.size()];
for(int i:intersectionSet){
intersection[len++] = i;
}
return intersection;
}
}
思路:HashMap 保存键值对<元素值,元素下标>
class Solution {
public int[] twoSum(int[] nums, int target) {
int res [] = new int [2];
if(nums ==null || nums.length == 0){
return res;
}
//一次性维护遍历过的元素值和它的下标 考虑到可以用HashMap
Map<Integer,Integer> hashMap = new HashMap<>();
for(int i = 0;i < nums.length;i++){
//遍历每一个值 计算target - 当前值的结果,若结果已经存在在哈希表中,那么返回下标,并记录当前元素的下标。
int temp = target - nums[i];
if(hashMap.containsKey(temp)){
res[0] = i;
res[1] = hashMap.get(temp);
}
hashMap.put(nums[i],i);
}
return res;
}
}
给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
思路:先计算 前两个数组nums1 和nums2的各个元素之和并将和记录为数组的键,出现的次数记为值。然后遍历num3和nums4,计算每一个元素的和,并查看 前两个数组的和中有无和为 - (num3[] + nums4[])的键,若有则记录次数
次数是将map中的次数进行累加,因为map中的键的每一个值和num3 nums4的一个和共同构成一个元组,而一个键对应出现次数代表有几种不同的元组。
class Solution {
public int fourSumCount(int[] nums1, int[] nums2, int[] nums3, int[] nums4) {
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i < nums1.length;i++){
for(int j = 0;j < nums2.length;j++ ){
int sum = nums1[i] + nums2[j];
if(map.containsKey(sum)){
map.put(sum , map.get(sum) + 1);
}else{
map.put(sum,1);
}
}
}
int count = 0;
for(int i = 0;i < nums3.length;i++){
for(int j = 0;j < nums4.length;j++ ){
int sum = nums3[i] + nums4[j];
if(map.containsKey(0-sum)){
//仔细思考
count += map.get(0-sum);
}
}
}
return count;
}
}
Given an integer array nums and an integer k, return true if there are two distinct indices i and j in the array such that nums[i] == nums[j] and abs(i - j) <= k.
思路:
1.从前向后遍历 若存在相等的元素 2.看下标满足i - j <=k 满足返回true
3.若不存在相等的元素,则将元素和下标存入哈希表中,若存在相等的元素 但不满足下标关系 保存下标最大的那个
如果在下标 i 之前存在多个元素都和 nums[i] 相等,为了判断是否存在满足 nums[i]=nums[j] 且 i−j≤k 的下标 j,应该在这些元素中寻找下标最大的元素,将最大下标记为 j,判断i−j≤k 是否成立。
如果i−j≤k,则找到了两个符合要求的下标 j 和 i;如果i−j>k,则在下标 i 之前不存在任何元素满足与 nums[i] 相等且下标差的绝对值不超过 k,理由如下:
假设存在下标 j’ 满足 j’ < j < i。
且nums[j’ ]=nums[j]=nums[i],则 i - j’ > i - j,由于 i - j > k,因此必有 i - j’ > k
class Solution {
public boolean containsNearbyDuplicate(int[] nums, int k) {
//1.从前向后遍历 若存在相等的元素
//看下标满足i - j <=k 满足返回true
Map<Integer,Integer> map = new HashMap<>();
for(int i = 0;i < nums.length;i++){
int temp = nums[i];
if(map.containsKey(temp) && (i - map.get(temp) <= k)){
return true;
}
//put在此映射中关联指定值与指定键。如果该映射以前包含了一个该键的映射关系,则旧值被替换。
//始终保存的都是重复元素的最大下标,
map.put(temp,i);
}
return false;
}
}