编程导航算法通关村第一关|白银

没有思路时,将常用的数据结构和算法都想一遍

public class ListNode {
    public int val;
    public Node next;
    Node(int x) {
        val = x;
        ext = null;//作用不大,写了更标准
    }
}

第一个公共子节点

way1:哈希和集合

先将一个链表元素存入map里,遍历第二个链表,看hash里面是否存在当前节点

public ListNode findFirstCommonNode(ListNode head1,ListNode head2){
    Set<ListNode> set=new HashSet<>();
    while(head1!=null){
        set.add(head1);
        head1=head1.next;
    }
    while(head2!=null){
        if(set.contains(head2))
            return head2;
        head2=head2.next;
    }
    return null;
}

way2:栈

需要两个栈,出栈,如果相等就继续出栈,直到找到最晚出栈的一种。

需要两个O(n)的空间,面试不占优势。

java里Stack的peek方法是返回栈顶的元素但不移除它。

import java.util.Stack;
public ListNode findFirstCommonNode(ListNode head1,ListNode head2){
    Stack<ListNode> stack1= new Stack();
    Stack<ListNode> stack2= new Stack();
    while(head1!=null){
        stack1.push(head1);
        head1=head1.next;
    }
    while(head2!=null){
        stack2.push(head1);
        head2=head2.next;
    }
    ListNode preNode=null;
    while(stack1.size()>0 &&stack2.size()>0){
        if(stack1.peek()==stack2.peek()){
            preNode=stack1.pop();
            stack2.pop();
        }else break;
    }
    retun preNode;
}

way3:拼接两个字符串

两个链表A,B

A:1-2-3-4-5

B:a-b-4-5

AB:1-2-3-4-5-a-b-4-5

BA:a-b-4-5-1-2-3-4-5

分别遍历即可找到

public ListNode findFirstCommonNode(ListNode head1,ListNode head2){
    if(head1==null||head2==null){
        return null;
    }
    ListNode p1=head1;
    ListNode p2=head2;
    while(p1!=p2){//相同时退出循环
        p1=p1.next;
        p2=p2.next;
        if(p1!=p2){
            //一个链表访问完了跳转到另外一个链表上
            if(p1==null) p1=head2;
            if(p2==null) p2=head1;
        }
    }
    return p1;
}

way4:差和双指针

第一遍遍历,得到两个链表的长度L1,L2。

第二遍遍历,长的先走|L2-L1|,然后一起走,相同的就是公共节点

public ListNode findFirstCommonNode(ListNode head1,ListNode head2){
    if(head1==null||head2==null){
        return null;
    }
    ListNode p1=head1;
    ListNode p2=head2;
    int l1==0,l2=0;
    //统计长度
    while(p1!=null){
        p1=p1.next;
        l1++;
    }
    while(p2!=null){
        p1=p2.next;
        l2++;
    }
    p1=head1;
    p2=head2;
    //长的先走x步
    if(l1>l2){
        int a=0;
        while(a<l1-l2){
            p1=p1.next;
            a++;
        }
    }else{
        int a=0;
        while(a<l2-l1){
            p1=p1.next;
            a++;
        }
    }
    //同时遍历
    while(p1!=p2){
        p1=p1.next;
        p2=p2.next;
    }
    return p1;
}

判断链表是否为回文序列

way1:

全部压栈,然后一边出栈一边比较。

public boolean isPalindrome(ListNode head){
    ListNode temp=head;
    Stack<Integer> stack= new Stack();
    while(temp!=null){
        stack.push(temp.val);
        temp=temp.next;
    }
    while(head!=null){
        if(head.val!=stack.pop()) return false;
        else head=head.next;
    }
    return true;
}

合并有序链表

合并两个

public ListNode mergeTwoLists(ListNode list1,ListNode list2){
    ListNode newHead =new ListNode(-1);
    ListNode res=newHead;
    while(list1!=null && list2!=null){
        if(list1.val<list2.val){
            newHead.next=list1;
            list1=list1.next;
        }else{
            newHead.next=list2;
            list2=list2.next;
        }
        newHead=newHeas.next;
    }
    while(list1!=null){
        newHead.next=list1;
        list1=list1.next;
        newHead=newHead.next;
    }
    while(list2!=null){
        newHead.next=list2;
        list2=list2.next;
        newHead=newHead.next;
    }
    return res.next;
}

优化

最终肯定只剩一个链表

public ListNode mergeTwoLists(ListNode list1,ListNode list2){
    ListNode newHead =new ListNode(-1);
    ListNode res=newHead;
    while(list1!=null && list2!=null){
        if(list1.val<=list2.val){
            newHead.next=list1;
            list1=list1.next;
        }else{
            newHead.next=list2;
            list2=list2.next;
        }
        newHead=newHeas.next;
    }
    newHead.next=list1==null?list2:list1;
    return res.next;
}

合并k个

先将前两个合并再合并k个

public ListNode mergeLists(ListNode[] lists){
    ListNode res=null;
    for(ListNode list:lists)
        res=mergeTwoLists(res,list);
    return res;
}

双指针

寻找中间节点

使用快慢指针,fast一次走两步,slow一次走一步

public static ListNode middleNode(ListNode head){
    ListNode slow=head,fast=head;
    while(fast!=null && fast.next!=null){
        slow=slow.next;
        fast=fast.next.next;
    }
    return slow;
}

寻找倒数第k个元素

快慢指针问题,让fast和slow相隔k个节点,然后一起向后,当fast为null时,slow就在倒数第k个节点的位置

public static ListNode getKthFromEnd(ListNode head, int k){
    ListNode slow=head,fast=head;
    while(fast!=null && k>0){
        fast=fast.next;
        k--;
    }
    while(fast!=null){
        fast=fast.next;
        slow=slow.next;
    }
    return slow;
}

将每个节点右移k个位置

way1:链表翻转

way2:双指针

k有可能大于链表长度,取模。

快指针先走k步,然后一起往后走,,快指针到末尾的时候,慢指针指的位置刚好是要断开的位置。

public static ListNode rotateRight(ListNode head, int k){
    if (head == null || k == 0)  return head;
    ListNode temp = head;
    ListNode fast = head;
    ListNode slow = head;
    int len = 0;
    while (head != null){
        head=head.next;
        len++;
    }
    if(k%len==0) return temp;
    while((k%len)>0){
        k--;
        fast=fast.next;
    }
    while(fast.next!=null){
        fast=fast.next;
        slow=slow.next;
    }
    ListNode res=slow.next;
    fast.next=temp;
    solw.next=null;
    return res;
}

删除链表元素

删除特定值

创建虚拟表头dummyHead,其next指向Head。通过cur.next.val判断,通过cur.next=cur.next.next删除。最后返回dummyHead**.next.**

public static ListNode removeElements(ListNode head, int val){
    ListNode dummyHead = new ListNode(0);
    dummyHead.next = head;
    ListNode cur=dummyHead;
    while(cur.next!=null){
        if(cur.next.val==val) cur.next=cur.next.next;
        else cur=cur.next;
    }
    return dummyHead.next;    
}

删除倒数第n个节点

way1:遍历

遍历得长度L,然后再遍历,在L-N+1处的元素是需要删除的

public static ListNode removeNthFromEndByLength(ListNode head, int n){
    ListNode dummy = new ListNode(0);
  	dummy.next = head;
    int length = getLength(head);//另外写的getLength函数
    ListNode cur = dummy;
    for (int i = 1; i < length - n + 1; ++i)
        cur = cur.next;
    cur.next = cur.next.next;
    ListNode res = dummy.next;
    return res;  
}

way 2:双指针

找倒数第k,同上。

public static ListNode removeNthFromEnd(ListNode head, int n){
    ListNode dummy = new ListNode(0);
  	dummy.next = head;
    ListNode fast=head;
    ListNode slow = dummy;
    for (int i = 0; i < n; ++i) 
        fast = fast.next;
    while(fast!=null){
        fast=fast.next;
        slow=slow.next;
    }
    slow.next=slow.next.next;
    ListNode res = dummy.next;
    return res;
}

删除重复元素

保留一个

已知链表按升序排列,遍历即可

public static ListNode deleteDuplicate(ListNode head){
    if(head==null) return head;
    ListNode cur=head;
    while(cur.next!=null){
        if(cur.val==cur.next.val) cur.next=cur.next.next;
        else cur=cur.next;
    }
    return head;
}

全都不要

public static ListNode deleteDuplicate(ListNode head){
    if(head==null) return head;
    ListNode dummy = new ListNode(0,head);
    ListNode cur=dummy;
    while(cur.next!=null && cur.next.next!=null){
        if(cur.next.val==cur.next.next.val) {
            int x=cur.next.val;
            while(cur.next!=null &&cur.next.val==x)
                cur.next=cur.next.next;
        }
        else cur=cur.next;
    }
    return dummy.next;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

endless_?

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值