一周刷爆LeetCode(跟着算法大神左神学习做的笔记)

一、异或

简言之,”异或“是一种逻辑运算,两个数字的二进制位对应位置相同时为0,不同时为1

满足交换律和结合律:

A ^ B = B ^ A
A ^ B ^ C = A ^ (B ^ C)

且0和任何运算元异或值为该运算元的值,两个相同运算元异或值为0.

0 ^ N = N
N * N = 0

以一段代码为例:

int a = 甲
int b = 乙

a = a ^ b // 步骤1

b = a ^ b // 步骤2

a = a ^ b // 步骤3

先揭晓答案,a最终结果为乙,b最终结果为甲。答对的朋友请跳过,答错的朋友请细细看来~

其中,经过步骤一后,a的值为甲^乙;b的值不变,为乙。

经过步骤二后,a的值不变,为甲^乙;b的值为(甲^乙)^乙,即甲^(乙^乙),即甲^0,即b的值为甲。

经过步骤三后,b的值不变,为甲;a的值为(甲^乙)^甲,即甲^甲^乙,即0^乙,即乙。

综上,最后运算结果为a = 乙,b = 甲。

左神讲了一道题真的还是挺经典的面试可能会问到的题。

(1)只有一种数出现了奇数次,找到数组中出现了奇数次的数。(同理:力扣136道)

假设存在数组a,找出数组a中出现了奇数次的数。

解决思路:使用异或。

A= [ a, b, c, d…… ]

int eor = 0

使eor和数组A中每个数都异或一遍,最终得到的结果即出现了奇数次的数。eor = a^b^c^d……

是不是很妙呢?开心消消乐,出现偶数次的都被消掉了~

(2)有两种不同数出现了奇数次,怎么找到这两种数?

假设数组中a和b出现了奇数次,a不等于b。int eor = 0

eor和数组中所有值异或,最终eor = a^b,那么怎么知道a和b的值呢?

因为a和b不同,那么eor某一位上一定是1,假设eor第八位是1,那么可以把数组分为两类:第八位上是1的数和第八位上是0的数。新设置一个变量eor',让eor'去异或第八位上是1的数,即可得到a或b中的一个,最后再用eor’去异或eor,得到另一个数。

(一个数)与(这个数取反+1)就可以提取出这个数最右侧的1。

二、插入排序

插入排序比冒泡排序和选择排序会好一点

代码实现如下:

public static void insertionSort(int[] arr){
        if(arr == null || arr.length < 2){
            return;
            }
//0~0是有序的 0~i想要有序
    for(int i = 1; i < arr.length; i++){
        for( int j = i - 1; j >= 0 && arr[j] > arr[j+1]; j--){
            swap(arr, j, j + 1);
            }
        }
}

三、二分法

说到二分法,大部分朋友应该很熟悉了:

class Solution {
    public int search(int[] nums, int target) {
        int left = 0, right = nums.length-1, mid;
        while(left<=right){
            mid = (left + right)/2;
            if(target == nums[mid]){
                return mid;
            }
            else if(target < nums[mid]){
                right = mid - 1;
            }
            else{
                left = mid + 1;
            }
        }
        return -1;
    }
}

先讲一个非常经典的题,有其他方法,这里讲的是使用二分法来做。

在排序数组中查找元素的第一个位置和最后一个位置。(力扣第34题)

给定一个按照升序排列的整数数组nums,和一个目标值target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值,返回[-1,-1]。

eg:[5, 7, 7, 8, 8, 9, 10] target=9   =====> 返回值[3, 4]

当然可以从数组左右遍历,遇到target值结束遍历,则左边遍历结束的位置为第一个位置,右边遍历结束的位置为最后一个位置。这个解法很简单,可以自己下去实现一下。

那么主要说说怎么用二分法来解决:

解题思路,找到两个位置,一个位置左侧区间的数字都小于target,一个位置右侧区间的数字都大于target。所以一个是找target所在的第一个位置,一个是找target所在的最后一个位置。那么怎么找到target出现的第一个位置呢?

1、target==nums[mid]的时候,target出现的第一个位置可能在mid,也可能在mid左边;

2、target<nums[mid]的时候,第一个位置在mid左边,那么使得right = mid;

3、target>nums[mid]的时候,第一个位置在mid右边,那么使得left = mid + 1;

待补充......

局部最小值问题:

在一个数组arr中,arr无序,但是任意两个相邻的数不相等,求任意一个局部最小数。(局部最小就是比左右两边的数字都要小,eg数组[7,5,6],5就是局部最小数。)

四、归并排序

相较于选择排序、冒泡排序经过多次比较只能得到一个值的位置,归并排序中比较的行为没有浪费,变成了整体有序的部分,然后跟下一个更大的部分merge!(经典)

详细讲解一下:

1)归并排序整体就是一个简单递归,左边排好序、右边排好序、让其整体有序

2)让其整体有序的过程用了排外序方法;

3)利用master公式来求解时间复杂度;

master公式为:

T(N) = a * T(N/b) + O(N^d)

4)归并排序的实质

时间复杂度O(N*logN),额外空间复杂度O(N)。

又讲到一道算法题,小和问题

在一个数组中,每一个数左边比当前数小的数累加起来,叫做这个数组的小和。求一个数组的小和。

eg:1,3,4,2,5 那么小和为:0+1+(3+1)+1+(1+3+2+4)= 16

简单的做法是,使用两个for循环,第一个for循环从右至左遍历,第二个for循环从左至右遍历,寻找比第一个for循环指的数字小的并且加入到sum中,以此类推。

下面讲解一下使用归并排序来解决这个问题:

归并排序的核心,先部分产生小和,再整体merge小和。整体过程如下图所示,我们把找左边比他小的数变为找右边有多少个数字比他大

经过步骤1,我们知道1右边有一个3比他大,所以这里有1个1;步骤2、3我们知道(1,3)右边4比他们大,所以这里有1个1,1个3;步骤4我们知道2右边6比他大,所以这里记录1个2;同理经过步骤5我们这里记录,2个1,1个3,1个4

最终结果为 1+1+3+2+2*1+3+4=16

我最开始这里也不太理解,后来懂了!因为有排序所以就能知道右边有多少个数字比他大了!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值