黄金挑战之双向链表

1.链表中环的问题

leetcode141:给定一个链表,判断链表中是否有环?

leetcode142:假如有环,那环的位置在哪里?

判断是否有环,最容易的方法就是使用hash,遍历的时候将元素放入map中,如果有环,一定会发生碰撞;发生碰撞的位置也就是入口位置。

public ListNode detectCycle(ListNode head){
    ListNode pos = head;
	Set<ListNode> visited = new HashSet<ListNode>();
	while(pos != null){
        if(visited.contains(pos)){
            return pos;
        }else{
            visited.add(pos);
        }
        pos = pos.next;
    }
	return null;
}

list集合中contains() 用于判断集合中 是否 包含指定的元素。list会将括号内的元素和list中存在的元素进行逐个比对,若有相等的,返回结果为true,若没有则返回结果为false。

Java中List.contains()方法比较的是地址而不是值

----------------------------------------------------------------------------------------------------------------

如果只有O(1)的空间该怎么做?

1.1为什么快慢指针一定会相遇?

确定是否有环,最有效的方法就是双指针,一个快指针(一次走两步),一个慢指针(一次走一步)。如果快指针能到达表尾就不会有环,否则如果存在环,慢指针在某个位置与快指针相遇。

public ListNode hasCycle(ListNode head){
    if(head == null || head.next == null){
        return false;
    }
	ListNode fast = head;
	ListNode slow = head;
	while(fast != null && fast.next != null){
        fast = fast.next.next
        slow = slow.next;
        if(fast == slow){
            return ture;
        }
    }
	return false;
}
1.2确定入口的方法

结论:先按照快慢的方式寻找到相遇的位置(假如为下图中的Z),然后将两指针分别放在链表头(X)和相遇位置(Z),并改为相同速度前进,则,两指针在环开始的位置(Y)相遇。

为什么呢?

假设一圈就相遇了,快指针在第二次进入环的时候就相遇了:

此时过程为:

1.找环中相遇点。分别用fast、slow表示快慢指针,slow每次走一步,fast每次走两步,直到在环中某个位置相遇,假设为(Z).

2.第二次相遇:

fast指针走了a+b+c+b步,slow指针走了a+b步;

那么2(a+b)=a+b+c+b => a=c;

因此我们让slow从Z开始走,fast回到起点,两个同时走(每次都走一步),那么他们就会在环的起点相遇。

如果多圈之后才相遇

设链表环外部分为a。slow指针进入环后,又走了b的距离与fast相遇,此时,fast已经走了n圈;

fast走的总距离为:a+n(b+c)+b 设环的长度为LEN = b+c

则a+n(b+c)+b = 2(a+b) a = c+(n-1)LEN

public ListNode detectCycle(ListNode head){
    if(head == null){
        return null;
    }
	ListNode slow = head,fast = head;
	while(fast != null){
        slow = slow.next;
        if(fast.next != null){
            fast = fast.next.next;
        }else{
            return null;
        }
        if(fast == slow){
            ListNode ptr = head;
            while(ptr != slow){
                ptr = ptr.next;
                slow = slow.next;
            }
            return ptr;
        }
    }
	return null;
}

2.双向链表

2.1基本概念

双向链表的结点:

class doubleNode{
    public int data;		//数据域
    public DoubleNode next;		//指向下一个结点
    public DoubleNode prev;		//指向前一个结点
    public DoubleNode(int data){
        this.data = data;
    }
    //打印结点的数据域
    public void displayNode(){
        System.out.print("{"+ data +"}");
    }
}

双向链表的结构和遍历的方法:

public class DoublyLinklist{
    private DoubleNode first;
    private DOubleNode last;
    public DoublyLinkList(){
        first = null;
        last = first;
    }
    //从头部开始打印
    public void displayForward(){
        System.out.print("List(first--->last):");
        DoubleNode current = first;
        while(current != null){
            current.displayNode();
            current = current.next;
        }
        System.out.println();
    }
    //从尾部开始打印
    public void displayBackward(){
        System.out.print("List(last--->first):");
        DoubleNode current = last;
        while(current != null){
            current.displayNode();
            current = current.next;
        }
        System.out.println();
    }
}
2.2链表类的定义以及方法:
public class LinkList{
    static class ListNode{//节点是链表的属性,所以用static来修饰
        //节点成员
        public int val;
        public ListNode prev;
        public ListNode next;
        public ListNode(int val){
            this.val = val;
        }
    }
    public ListNode head;
    public ListNode last;

    //头插法
    public void addFirst(int data){}

    //尾插法
    public void addLast(int data){}

    //在任意节点位置插入,假设第一个节点下标为0
    public void addIndex(int index,int data){}

    //查找是否包含关键字key是否在单链表中
    public boolean contains(int key){}

    //删除第一次出现关键字为key的节点
    public void remove(int key){}

    //删除所有值为key的节点
    public void removeAllKey(int key){}

    //得到链表的长度
    public int size(){}

    //打印链表内容
    public void display(){}

    //删除链表
    public void clear(){}
}

头插法addFirst():

public void addFirst(int data){
	ListNode node = new ListNode(data);//创建一个节点
	if(head == null){//无元素情况
        head = node;
        last = node;
        return;
    }
	head.prev = node;
	node.next = head;
	head = node;
}

尾插法addLast():

public void addLast(int data){
    ListNode node = new ListNode(data);
	if(head == null){
        head == node;
        last == node;
        return;
    }
	last.next = node;
	node.prev = last;
	last = node;
}

链表长度size()

public int size(){
    ListNode cur = head;
    int count = 0;
    while(cur != null){
        cur = cur.next;
        count++;
    }
    return count;
}

打印链表内容display()

public void display(){
    ListNode cur = head;
    while(cur != null){
        System.out.print(cur.val + " ");
        cur = cur.next;
    }
    System.out.println();
}

删除链表clear()

public void clear(){
    ListNode cur = head;
    while(cur != null){
        ListNode curNext = cur.next;
        cur.prev = null;
        cur.next = null;
        cur = curNext;
    }
    head = null;
    last = null;
}

在任意位置插入addIndex()

1.先看输入的下标是否合法

2.看下标是否是头结点,尾节点,是的话直接调用对应方法

3.如下图

public void addIndex(int index,int data){
    if(index < 0 || index > size()){
        System.out.print("index输入不合法");
    }
    if(index == 0){
        addFirst(data);
        return;
    }
    if(index == size()){
        addLast(data);
        return;
    }
    //用cur找到插入位置
    ListNode cur = head;
    while(index != 0){
        cur = cur.next;
        index--;
    }
    ListNode node = new ListNode(data);
    node.prev = cur.prev;
    node.next = cur;
    cur.prev.next = node;
    cur.prev = node;
}

链表当中是否有key contains(key)

public boolean contains(int key){
    ListNode cur = head;
	while(cur != null){
        if(cur.val == key){
            return ture;
        }
        cur = cur.next;
    }
	return false;
}

删除链表中第一次出现的key remove(int key)

1.先找到key所在的位置

2.判断是否是头结点,如果是且不只有一个元素

如果是头结点且只有一个元素

3.再判断是否是尾节点,如果是:

4.如果是中间节点:

public void remove(int key){
	ListNode cur = head;
	while(cur != null){
        if(cur.val == key){
            if(cur == head){
                head = head.next;
                if(head == null){//只有一个元素
                    last = null;
                }else{//有多个元素
                    head.prev = null;
                }
            }else{
                if(cur == last){//尾节点
                    last = last.prev;
                    last.next = null;
                }else{//中间节点
                    cur.prev = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            return;
        }
        cur = cur.next;
    }
}

删除所有值为key的节点 removeAllKey(int key)

和上面删除一个一样,就是删除完后不要return

public void remove(int key){
	ListNode cur = head;
	while(cur != null){
        if(cur.val == key){
            if(cur == head){
                head = head.next;
                if(head == null){//只有一个元素
                    last = null;
                }else{//有多个元素
                    head.prev = null;
                }
            }else{
                if(cur == last){//尾节点
                    last = last.prev;
                    last.next = null;
                }else{//中间节点
                    cur.prev = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            //return;
        }
        cur = cur.next;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值