java学习笔记(7)--链表

标签(空格分隔):笔记


java其实已经将很多底层的数据结构进行了封装,虽然工作用不到,但是笔试和面试问的还是比较频繁的,而且这种面试题还是直接手撕代码,故专门总结一下。


1. 概念

1.1 链表(Linked list)


是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer)。使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。

1.2 单向链表(Single-Linked List)


  1. 单链表是链表中结构最简单的。一个单链表的节点(Node)分为两个部分,第一个部分(data)保存或者显示关于节点的信息,另一个部分存储下一个节点的地址。最后一个节点存储地址的部分指向空值。
  2. 单向链表只可向一个方向遍历,一般查找一个节点的时候需要从第一个节点开始每次访问下一个节点,一直访问到需要的位置。而插入一个节点,对于单向链表,我们只提供在链表头插入,只需要将当前插入的节点设置为头节点,next指向原头节点即可。删除一个节点,我们将该节点的上一个节点的next指向该节点的下一个节点。

2. 链表的java实现


2.1 设计一个接口,定义链表需要哪些操作(已通过验证,放心用)

public interface ILinkedList {
    //添加元素
    boolean addItems(Object obj);
    //删除元素
    boolean deleteItems(Object obj);
    //删除头元素
    boolean deleteheadItems();
    //查找
    Object findItems(Object obj);
    //返回长度
    int length();
    //展示所有元素
    void display();
    //判断是否为空
    boolean isEmpty();
}

2.2 定义一个单链表,实现上述接口的操作(已通过验证,放心使用)

public class NewSingleList implements ILinkedList{
    private int size;//链表长度
    private Node head;//链表的头结点,因为是成员变量,默认为null
    //
    //定义一个成员内部类Node,包含头部数据和next指向的对象
    public class Node{
        private Object data;
        private Node next;
        public Node(Object data, Node next) {
            this.data = data;
            this.next = next;
        } 
    }
    //添加
    @Override
    public boolean addItems(Object obj) {
        Node n1=new Node(obj,null);
        if(isEmpty()) {
            head=n1;
            size++;
            return true;
        }
        else {
            n1.next=head;//头插法,从头部插入,新插入的节点作为新的head
            head=n1;
            size++;
            return false;
        }
    
    }
//删除元素
    @Override
    public boolean deleteItems(Object obj) {
        if(isEmpty()) {
            return false;
        }
        if(head.data.equals(obj)) {
            deleteheadItems();//调用删除头部元素的函数
            return true;
        }
        //定义当前节点的前一个节点(初始的时候下面两个起点相同,但是循环一遍之后位置就变了,可以自己试试)
        Node previous=head;
        Node current=head;//定义当前节点
        while(current!=null) {
            if(current.data.equals(obj)) {
                previous.next=current.next;//删除核心代码
                size--;
                return true;
            }
            else {
                previous=current;
                current=current.next;
            }
            
        }
        return false;
            
    }
        
    //删除头部元素
    @Override
    public boolean deleteheadItems() {
        if(isEmpty()) {
            return false;
        }
        else {
            head=head.next;
            size--;
            return true;
        }
        
    }
//查找
    @Override
    public Object findItems(Object obj) {
        if(isEmpty()) {return false;}
        Node tmp=head;
        while(tmp!=null) {
            if(tmp.data.equals(obj)) {return true;}
            else {
                tmp=tmp.next;
            }
        }
        return false;
    }
//返回链表长度
    @Override
    public int length() {
        
        return size;
    }
//展示元素
    @Override
    public void display() {
        Node tmp=head;
        while(tmp!=null) {
            System.out.println(tmp.data);
            tmp=tmp.next;   
        }
        
    }
//判断是否为空
    @Override
    public boolean isEmpty() {
        if(size==0) {
            return true;
        }
        return false;
    }

}

2.3 对这个链表进行测试(已通过验证,放心使用)

public class ListTest01 {
    public static void main(String[] args) {
        NewSingleList l1=new NewSingleList();
        l1.addItems(4);
        l1.addItems(7);
        l1.addItems(66);
        l1.addItems(233);
        l1.deleteItems(4);
        l1.display();
    }

}

3. 链表的常见面试题

3.1 判断链表是否有环

//点击上述题目链接去LeetCode刷题。第141题
//方法一:双指针,只要有环,快的指针总会赶上慢的指针,空间复杂度O(1)
public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head==null || head.next==null){
            return false;
        }
        ListNode l1=head;
        ListNode l2=head.next;
        while(l1 !=null && l2 !=null && l2.next!=null){
            if(l1==l2){
                return true;
            }
            l1=l1.next;
            l2=l2.next.next;
            
        }
        return false;
    }
}

//如果需要让你用一个指针,检查一个结点此前是否被访问过来判断链表是否为环形链表。常用的方法是使用哈希表。时间和空间复杂度都是O(n)
public class Solution {
    public boolean hasCycle(ListNode head) {
       Set<ListNode> l1=new HashSet<>();
       while(head != null){
           if(l1.contains(head)){
           return true;
           }
           else{
               l1.add(head);
           }
           head=head.next;

       }
       return false;
    }
}

3.2 找出链表交点

//链表的交点难点在于链表的长度不同,当同时进行移位的时候,由于长度不同,可能会导致相同的点完美错过

//例如两个链表,一个是1-2-5-7-8-1,一个是4-3-8-1,
//当这两个同时从头开始时,如果list1=list1.next;list2=liat2.next;当list2指向8的时候,list1刚指向5,显然不行。
//但是如果把两个拼接起来,二者等长,变成1-2-5-7-8-1-4-3-8-1和 4-3-8-1-1-2-5-7-8-1那就容易多了



//      1-2-5-7-\    /-1-2-5-7-8
//               8-1-
//          4-3-/    \4-3-8

 public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
         ListNode l1 = headA, l2 = headB;
        while (l1 != l2) {
        l1 = (l1 == null) ? headB : l1.next;
        l2 = (l2 == null) ? headA : l2.next;
        }
        return l1;
        }
}

3.3 链表翻转

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev=null;
        ListNode cur=head;
        
            while(cur !=null){
                ListNode nextTemp = cur.next;
                cur.next = prev;
                prev = cur;
                cur = nextTemp;
                
        }
        return prev;
        
    }

}

猜你喜欢

转载自www.cnblogs.com/starstrrys/p/11963627.html