算法学习day01(二分\双指针\滑动窗口\链表)

一、二分法

首先,二分法搜索的前提是数组必须是有序的。然后在一个有序的数组里面找到目标值。

while(left<=right){

    target<nums[mid] 更新右边界 right=mid-1

    target>nums[mid] 更新左边界 left=mid+1

    如果相等,说明找到了,return mid;

}

注意的点:while循环中的条件是影响到下面更新边界操作的。借助一下开闭区间来理解(卡尔那边学到的)

1.如果是left<=right,说明左右结点是可以相等的,这个区间是[left right]的,那么在更新边界的时候,因为边界值都是可以取到的(闭区间),所以left=mid+1或者right=mid-1,都是允许的。

2.如果left<right,说明左右结点不可以相等,也就是说区间是[left right)的。那么在更新边界的时候,左边界是闭的可以取到值,而右边界是开的不可以取到

因此在更新右边界right的时候要变成mid,这样[left,mid),才可以取到mid-1。

更新左边界left的时候,因为是闭的,所以mid+1,仍然可以。

为了方便,推荐while用<=条件来写,这样就不用考虑能不能取到边界值了。

二、移除元素(双指针):

这里的双指针都是从同一个起点开始的。

使用快慢指针,其实是对数组起到了一个筛选的作用。筛选的条件就是值是否等于val,如果等于就被淘汰。我觉得快指针主要起筛选的作用,慢指针起一个保存位置的作用。

for(int fast=0;fast<nums.length;fast++){

    //开始筛选

    if(nums[fast]!=val){

            nums[slow++]=nums[fast];成功筛选

        }

}

三、有序数组的平方(双指针):

这里的双指针是从两边开始的。(是基于题目所给的数组是非递减排序的,并且存在负数)

从这里我们可以推断出:两边的值的平方一定是最大的,并且从两边到中间是从大到小的。

思路:类似于合并排序,比较两边平方大小,大的放进去并且移动。

if(nums[slow]*nums[slow]>nums[fast]*num[fast])

result[size--]=nums[slow++]*nums[slow]

四、长度的最小子数组:(暴力法/滑动窗口)

暴力法:

对该题的一些自我理解:因为要求的的最小子数组,因此要求是连续的。可以使用暴力解法:依次从i开始,只要sum>s,就算出此时子数组的长度,并且更新。

    public int minSubArrayLen(int target, int[] nums) {

        int subLength;      

        Integer result=Integer.MAX_VALUE;

        for(int i=0;i<nums.length;i++){

            int sum=0;

            for(int j=i;j<nums.length;j++){

                sum+=nums[j];

                if(sum>=target){

                    subLength=j-i+1;//当sum大于target的时候 那么我的长度为j-i+1

                    result=subLength<result?subLength:result;

                    break;//因为要子数组 所以以i为开头的子数组一旦满足 就break

                }

            }

        }

        if(result==Integer.MAX_VALUE)

        return 0;

        else return result;

    }

但是使用暴力法时间复杂度为O(n*n)会导致时间超出限制。因此使用O(n)的滑动窗口法

滑动窗口(双指针):个人理解:依旧是一个i慢指针、j快指针。在i和j移动的过程中按照sum>target形成滑动窗口。j是用来确定滑动窗口的右边界,确保sum>target。i是用来不断寻找最小长度。

代码:

int i=0;

for(int j=0;j<nums.length;j++){

    sum+=nums[j];

    while(sum>=target){

            int subL=j-i+1;

            result=subL<result?subL:result;

            //从这里开始 慢指针开始优化subL

           sum-=nums[i];i++;

}

}

五、螺旋矩阵:重点:旋转的次数(也是循环的次数)是(n/2)

首先要确定一个规则,当每一行的最后一个不由自己处理,交给下一个处理,这样每一次都处理相同的个数,for循环就统一了。当填写完一圈之后,准备填写第二圈之前,要将起始点的坐标和每一次终止的位置进行更新。(犯的错误,从左到右和从上到下的时候,循环的终止条件是<n-count 从右到左和从下到上的时候,循环的终止条件应该是>startX/startY)

  public int[][] generateMatrix(int n) {

        int [][]martix=new int[n][n];

        int startX=0;

        int startY=0;

        int offSet=1;//每次循环要减的值

        int cnt=1;

       

        //开始给矩阵填值

        while((n/2)!=0){

            //从左到右

            int i=startX;//行索引

            int j=startY;//列索引

            for(;j<n-offSet;j++){

                martix[i][j]=cnt++;

            }

            //从上到下

            for(;i<n-offSet;i++){

                martix[i][j]=cnt++;

            }

            //从右到左

            for(;j>startY;j--){

                martix[i][j]=cnt++;

            }

            //从下到上

            for(;i>startX;i--){

                martix[i][j]=cnt++;

            }

            startX++;

            startY++;

            offSet++;

            n/=2;

        }

        if(n%2==1){

            martix[startX][startY]=cnt;

        }

        return martix;

    }

六:移除链表元素(较简单)

我认为,链表和数组的结构是类似的,只不过前者的空间地址不同,需要通过next链接。并且增删元素比较容易,但是查找的话后者更容易。难点:最后返回的时候一定是虚拟头结点的next,不能是head,因为head也在需要移除的范围里面.比如这个例子

七:设计链表()

遇到的问题:

1.在获取第下标为index值的时候,不知道该如何遍历(怕遍历到下一个或者是遍历到上一个,这种问题经常困扰我),这里我选择从head开始,也就是从第一个结点,下标为0,我要获取下标为index的结点,那么就需要遍历到index,因此使用一个for循环就可以。

for(int i=0;i<=index;i++)

cur=cur.next;

2.在添加结点或者删除结点的时候,一定要更新size

八、反转链表(双指针/递归)

刚开始我在思考的时候:想到了slow fast指针同时后移动,但是连接的时候没有想到用temp保存fast.next;

双指针

slow fast temp

while(fast!=null){

     temp=fast.next;//存储一下

     fast.next=slow;

     slow=fast;

     fast=temp;

}

return slow;

递归法:

道理和双指针类似

    public ListNode reverse(ListNode slow,ListNode fast){

        if(fast==null)return slow;

        ListNode temp=new ListNode();

        temp=fast.next;

        fast.next=slow;

        reverse(fast,temp);

    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值