牛客101算法【4部分】

1、链表【翻/环/倒k/删重/合并/重排】

回文/相加/排序

BM1:翻转链表【✅】

    public ListNode ReverseList(ListNode head) {
        ListNode p=head;
        ListNode pre=null;
        while(p!=null)
        {
            ListNode q=p.next;
            p.next=pre;
            pre=p;
            p=q;
        }
        return pre;
    }

1、特殊情况{1}:p都是指向null(换个头)
2、特殊情况 null:while 跳过,直接输出 pre=null
3、while(p!=null):p每次都是截断后的下一个。pre是结尾。所以在while中取q。


BM2:链表内指定区间反转【⭐】

public ListNode reverseBetween (ListNode head, int m, int n) {
        //1.{} {1} m<=n
        if(head==null||head.next==null||m==n)
        return head;
        //2.从头开始
        ListNode pre=null;
        ListNode p=head;
        ListNode PRE=null;//【ERRPR】PRE=head
        int count=1;
        //3.从中间开始(默认m
        if(m!=1){
            while(count!=m){
                pre=p;
                p=p.next;
                count++;
            }
            PRE=pre;
            pre=p;
            p=p.next;count++;
        }
        while(count!=n+1){//【ERRPR】n+1
            ListNode q=p.next;
            p.next=pre;
            pre=p;
            p=q;
            count++;
        }
        if(PRE!=null) {
        PRE.next.next=p;
        PRE.next=pre;
        return head;
        }
        else{
            head.next=p;//【ERRPR】PRE.NEXT
            return pre;
        }
    }


BM3:链表中的节点每k个一组翻转【】


BM4:合并两个排序的链表

    public ListNode Merge(ListNode list1,ListNode list2) {
        ListNode p1=list1;
        ListNode p2=list2;
        ListNode h=new ListNode(-1);//定义表头
        ListNode q=h;
        while(p1!=null&&p2!=null){
            if(p1.val<=p2.val){
                q.next=p1;//h表和q1,q2表还有联系,前置节点没有断,没关系
                p1=p1.next;
            }
            else{
                q.next=p2;
                p2=p2.next;
            }
            q=q.next;
            q.next=null;
        }
        if(p2!=null)//直接赋值
        p1=p2;
        q.next=p1;
        return h.next;//直接这样输出就行
    }

BM5:合并k个已排序的链表【】


BM6:判断链表中是否有环【⭐】

1、HashSet

import java.util.*;
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null||head.next==null)
        return false;
        Set<ListNode> set=new HashSet<ListNode>();
        ListNode p=head;
        while(p!=null){
            if(set.contains(p))
            return true;
            set.add(p);
            p=p.next;
        }
        return false;
    }
}

2、《快慢指针》fast快走两步,fast和slow有坏总会相遇

    public boolean hasCycle(ListNode head) {
        //1.{},{1}
        if(head==null||head.next==null)
        return false;
        //2.{1,2,null}+{1,2,3,4..}
        ListNode fast=head;
        ListNode slow=head;
        while(fast!=null&&fast.next!=null)//【ERROR】判断条件是为进出服务的
        {
            fast=fast.next.next;//是一直快走两步
            slow=slow.next;
            if(fast==slow)
            return true;
        }
        return false;
    }

BM7:链表中环的入口结点【⭐】

HashSet

import java.util.*;
public class Solution {
    public ListNode EntryNodeOfLoop(ListNode pHead) {
        if(pHead==null||pHead.next==null)
           return null;
        Set<ListNode> set=new HashSet<ListNode>();
        ListNode p=pHead;
        while(p!=null)
        {
            if(set.contains(p))
            return p;
            set.add(p);
            p=p.next;
        }
        return null;
    }
}

BM8:输出链表中倒数最后k个结点【✅】

一个指针提前k几步另一个再走

    public ListNode FindKthToTail (ListNode pHead, int k) {
        //特殊情况
        if(pHead==null)
        return null;
        ListNode p=pHead;
        int count=1;
        //【p!=null要放前面限制k】
        //【count<=k】
        while(p!=null&&count<===k){
            count++;
            p=p.next;
        }
        if(count<=k&&p==null)//【ERROR拉下了这种情况】
        return null;
        ListNode pre=pHead;
        while(p!=null){
            pre=pre.next;
            p=p.next;
        }
        return pre;
    }

BM9:删除链表的倒数第n个节点【⭐】

区别于输出:因为用到了前置结点

    public ListNode removeNthFromEnd (ListNode head, int n) {
        // 特殊情况
        if(head==null)
        return null;
        ListNode p=head;
        int count=1;
        ListNode h=new ListNode(-1);//【因为要用到前置】
        h.next=head;//【不是用h走的】
        ListNode pre=h;
        while(p!=null&&count<=n){//【判断条件是一样的:因为多了前置,所以这里不变】
            p=p.next;
            count++;
        }
        if(p==null&&count<=n)
        return null;
        while(p!=null)//【ERRPR:不用判断p.next】
        {
            p=p.next;
            pre=pre.next;
        }
        pre.next=pre.next.next;
        return h.next;
    }

BM10:两个链表的第一个公共结点【⭐】

画图

        //特殊情况:{}
        if(pHead1==null||pHead2==null)
        return null;
        ListNode p1=pHead1;int k1=0;
        ListNode p2=pHead2;int k2=0;
        while(p1!=null){
            p1=p1.next;
            k1++;
        }
        while(p2!=null){
            p2=p2.next;
            k2++;
        }
        p1=pHead1;
        p2=pHead2;
        if(k1>k2){
            p1=pHead1;
            for(int i=1;i<=k1-k2;i++)
            p1=p1.next;
        }
        if(k1<k2){
            for(int i=1;i<=k2-k1;i++)
            p2=p2.next;
        }
        while(p1!=null&&p2!=null){
            if(p1==p2)
            return p1;
            p1=p1.next;
            p2=p2.next;
        }
        return null;
    }

BM11:链表相加(二)【】

一个指针提前k几步另一个再走


BM12:单链表的排序[]

提示:这里可以添加计划学习的时间


BM13:判断一个链表是否为回文结构[]

提示:这里可以添加计划学习的时间


BM14:链表的奇偶重排

提示:这里可以添加计划学习的时间

    public ListNode oddEvenList (ListNode head) {
        if(head==null)
        return null;
        int count=1;
        ListNode h=head;
        ListNode pHead=new ListNode(-1);ListNode p=pHead;
        ListNode qHead =new ListNode(-1);ListNode q=qHead;
        while(h!=null)
        {
            if(count%2!=0){
              p.next=h;h=h.next;//【ERROR:h要紧跟后面】
              p=p.next;

              p.next=null;
            }
            else{
              q.next=h;h=h.next;//【ERROR:h要紧跟后面】
              q=q.next;
              q.next=null;
            }
            count++;
        }
        p.next=qHead.next;
        return pHead.next;
    }

BM15:删除有序链表中重复的元素-留一个【⭐】

自己走:不合适就删掉下一个

    public ListNode deleteDuplicates (ListNode head) {
        //特殊情况{} {x}
        if(head==null||head.next==null)
        return head;
        //一般情况
        ListNode p=head;
        while(p.next!=null)
        {
            if(p.val==p.next.val)
            p.next=p.next.next;
            else
            p=p.next;
        }
        return head;
    }

BM16:删除有序链表中重复的元素-全删【⭐】

1、前置节点:信任了再加上
2、HashMap记数:次数大于1 的全部删除

    public ListNode deleteDuplicates (ListNode head) {
        if(head==null||head.next==null)
        return head;
        HashMap<Integer,Integer>map=new HashMap<Integer,Integer>();
        ListNode p=head;
        while(p!=null){
            if(map.containsKey(p.val))
            map.put(p.val,map.get(p.val)+1);
            else
            map.put(p.val,1);
            p=p.next;
        }
        ListNode h=new ListNode(1);
        h.next=head;
        p=h;
        while(p.next!=null)
        {
            if(map.get(p.next.val)>1)
            p.next=p.next.next;//【用p自己就行,不用另外定义】
            else
            p=p.next;

        }
        return h.next;
    }

1、二分查找排序【数组有序首先考虑】

BM17:二分查找-I【✅】

元素升序的、无重复数字

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

BM18:二维数组中的查找

每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序

        for(int[] arr:array)//数组的东西
        {
            int left=0;
            int right=arr.length-1;
            while(left<=right)
            {
               int mid=(left+right)/2;
               if(target==arr[mid])
               return true;
               if(target>arr[mid])
               left=mid+1;
               else
               right=mid-1;
            }
        }
        return false;
    }

BM19:寻找峰值【⭐】

每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序

    public int findPeakElement (int[] nums) {
        int left=0;
        int right=nums.length-1;
        while(left<right)
        {   
            int mid=(left+right)/2;
            if(nums[mid]>nums[mid+1])//【mid-1有<0的风险,所以和mid+1比较】
            right=mid;//【区间内递减,right变成可能的峰值】
            else
            left=mid+1;//【区间内增,left变成可能的峰值】
        }
        return right;//【输出left/right都行】
    }

BM20:数组中的逆序对-归并排序-递归【⭐】

分[分解]-治[修补] 归并排序

public class Solution {
    int count=0;//全局操作的参数
    //0.程序入口
    public int InversePairs(int [] array) {
       if(array.length<2)
       return 0;
       mergesort(array,0,array.length-1);
       return count; 
    }
    //1,归并排序【递归】
    public void mergesort(int []array,int left,int right)
    {
        //终止条件
        if(left>=right)
        return;
        //递归操作(先序树)
        int mid=left+(right-left)/2;///2
        mergesort(array,left,mid);
        mergesort(array,mid+1,right);
        merge(array,left,mid,right);//在前一层级上收拾count(每组拍好了)
    }
    //2.收拾count
    public void merge(int []array,int left,int mid,int right)
    {
        //1、左右数组重新排序组装到排序数组
        int arr[]=new int[right-left+1];//排序数组
        int index=0;//排序数组下标记
        int l=left;//左待排序数组首下标
        int r=mid+1;//右待排序数组首下标
        //left---mid;mid+1---right(l--r)
        while(l<=mid&&r<=right)
        {
            //l)一开始就小于r时
            if(array[l]>array[r])
            {
                arr[index]=array[r];
                r++;
                count+=mid+1-l;
                count=count%1000000007;
            }
            //2)L比r大
            else
            {
                arr[index]=array[l];
                l++;
            }
            index++;
        }
        while(l<=mid)
        arr[index++]=array[l++];
        while(r<=right)
        arr[index++]=array[r++];

        //2、排序后的数组复制到原数组
        int s=left;//重新赋值原赋数组
        for(int t:arr)
        array[s++]=t;
    }
}


BM21:旋转数组的最小数字[x]


BM22: 比较版本号【⭐xx】


1、双指针

BM 87:合并两个有序数组

import java.util.*;
public class Solution {
    public void merge(int A[], int m, int B[], int n) {
        if(m==0){
            for(int i=0;i<n;i++)//【不可以A=B】
            A[i]=B[i];
            return;
        }
        if(n==0)
        return;
        int C[]=new int[m+n];//可以用形参定义数组大小
        int c=0;
        int a=0;
        int b=0;
        while(a<=m-1&&b<=n-1){
            if(A[a]<=B[b]){
                C[c]=A[a];
                a++;
            }
            else{
                C[c]=B[b];
                b++;
            }
            c++;
        }
        while(a<=m-1){
            C[c]=A[a];
            a++;
            c++;
        }
        while(b<=n-1){
            C[c]=B[b];
            b++;
            c++;
        }
        for(int i=0;i<C.length;i++)
        A[i]=C[i];

    }
}

BM 88: 判断是否为回文字符串

String和char数组的转换

import java.util.*;
public class Solution {
    public boolean judge (String str) {
        // write code here
        if(str.length()<=1)
        return true;
        char c[]=str.toCharArray();
        for(int i=0;i<=(c.length-1)/2;i++)//【i<=(c.length-1)/2】
        {
            if(c[i]!=c[c.length-1-i])
            return false;       
        }
        return true;
    }
}

BM 89: 合并区间【⭐】

import java.util.*;
public class Solution {
    public ArrayList<Interval> merge(ArrayList<Interval> intervals) {
        ArrayList<Interval> list=new ArrayList<Interval>();
        if(intervals.size()<=1)
        return intervals;
        //【集合元素的自定义排序:按照先排左再排右节点的方式排序】
        Collections.sort(intervals,new Comparator<Interval>(){
            public int compare(Interval a,Interval b)
            {
                if(a.start!=b.start)
                return a.start-b.start;
                else
                return a.end-b.end;
            }
            });
            int k=0;
            //1、先放进去1个:index=k;
            list.add(intervals.get(0));
            for(int i=1;i<intervals.size();i++)
            {
                Interval x=list.get(k);
                //2.再取下一个待比较的
                Interval y=intervals.get(i);
                //1)先比较有交集的
                if(y.start<=x.end)
                {
                    if(y.end<=x.end) //全部包含
                    continue;
                    else //部分包含
                    {
                        Interval temp=new Interval(x.start,y.end);
                        list.remove(k);
                        list.add(temp);
                    }
                }
                //2)没有交集的直接放进去
                else
                {list.add(y);
                k++;
                }
            }
        return list;
    }
}

BM 90:最小覆盖子串【X】


BM 91: 反转字符串

StringBuffer

import java.util.*;
public class Solution {
    public String solve (String str) {
        StringBuffer s=new StringBuffer();
        s.append(str);
        return s.reverse().toString();
    }
}

BM 92:最长无重复子数组【⭐】

队列

import java.util.*;
public class Solution {
    public int maxLength (int[] arr) {
        Queue<Integer>queue=new LinkedList<Integer>();
        int max=0;
        for(int i=0;i<arr.length;i++){
            while(queue.contains(arr[i])){//【!while】
                queue.poll();
            }
            queue.add(arr[i]);
            max=Math.max(max,queue.size());
        }
        return max;
    }
}

BM 93:盛水最多的容器【⭐】

思想没看那

import java.util.*;
public class Solution {//思想没看
    public int maxArea (int[] height) {
        if(height.length<2)
        return 0;
        int area=0;//面积
        int left=0;//左边
        int high=height.length-1;//右边
        while(left<high)
        {
            //比较:area和底*最小侧边
            area=Math.max(area,(high-left)*Math.min(height[left],height[high]));
            if(height[left]<height[high])//小的移动
            left++;
            else
            high--;
        }
        return area;
    }
}

BM 94:接雨水问题【X】


2、堆栈

2、哈希

2、字符串

3、二叉树【5.14】

3、递归

3、动态规划

3、贪心【5.20】

4、模拟

你可能感兴趣的:(算法,链表,数据结构)