链表---数据结构-黑马

链表

定义

链表是数据元素的线性集合,其每个元素都指向下一个元素,元素存储上是不连续的

分类

  • 单向链表,每个元素只知道自己的下一个元素是谁。
    在这里插入图片描述
  • 双向链表,每个元素知道自己的上一个元素和下一个元素。
    在这里插入图片描述
  • 循环链表,通常链表尾部节点 tail 指向的为null,而循环链表的tail指向链表的头部节点 head。
    在这里插入图片描述

链表中还有一种特殊的节点称之为,哨兵(Sentinel)节点,也称之为 哑元(Dummy)节点, 不存储数据,一般作为头尾,简化边界判断,如下如所示。
在这里插入图片描述

性能

随机访问

根据 i n d e x index index 查询,时间复杂度为 O ( n ) O(n) O(n)

插入或删除

  • 起始位置,时间复杂度为, O ( 1 ) O(1) O(1)
  • 结束位置,若已知尾部节点 tail ,时间复杂度为, O ( 1 ) O(1) O(1) ;否则为 O ( n ) O(n) O(n)
  • 中间位置,根据 index 查找的时间复杂度 + O ( 1 ) O(1) O(1)

单项链表实现

方法实现

节点类、头部添加、循环遍历、尾部添加

// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {
    
    private Node head = null;  // 头指针
    
    // 节点类
    private static class Node() {
        int value;  // 值
        Node node;  // 下一个节点
        
        public Node(int value, Node node) {
            this.value = value;
            this.node = node;
        }
    }
    
    @Override
    public Iterator<Integer> iterator() {
        // 匿名内部类 -> 带名字的内部类
        return new NodeIterator();
    }
    
    // 1.链表头部添加元素
    public void addFirst(int value) {
        // 1.当链表为空时
        // head = new Node(value, null);  因为 head = new Node(value, head);这行代码可以处理链表为空的情况,则注释
        // 2.当链表非空时
        head = new Node(value, head);
    }
    
    // 寻找最后一个节点
    private Node findLast() {
        // 链表为空
        if (head == null) {
            return null;
        }
        Node p;
        for(p = head; p.next != null; p = p.next) {
            
        }
        return p;
    }
    
    // 3.尾部添加元素
    private void addLast(int value) {
        Node last = findLast();
        if (last == null) {
            // 头部添加
            addFirst(value);
            return;
        }
        last.next = new Node(value, null);
    }
    
    // 4.根据索引值查找结点
    public Node findNode(int index) {
        int i = 0;
        for(Node p = head; p != null; p = p.next, i++) {
            if (index == i) {
                return p;
            }
        }
        // 未找到
        return null;
    }
    
    // 5.根据index找到对应节点,并返回节点值
    public int get(int index) {
        Node p = findNode(index);
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index))}
        return p.value;
    }
    
    // 6.向索引位置插入元素
    public void insert(int index, int value) {
        if (index == 0) {  // 索引为0,向链表头部添加
            addFirst(value);
            return;
        }
        // 1.找到插入索引位置的上一个节点
        Node p = findNode(index - 1);
        // 判断p节点是否为空
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node n = p.next;
        // p存在的情况
        p.next = new Node(value, n);
    }
    
    // 7.删除头部节点
    public void removeFirst() {
        if (head == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        // 头节点的下一个节点赋给头
        head = head.next;
    }
    
    // 8.按索引位置删除节点
    public void remove (int index) {
        if (index == 0) {
            removeFirst();
            return;
        }
        Node p = findNode(index - 1);  // 找到待删除节点的上一个节点
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node n = p.next;               // 待删除节点
        if (n == null) {
            // 未找到待删除节点
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        p.next = n.next;			   // 待删除节点的上一个节点p的next指向待删除结点的下一个节点
    }
    
    // 6.遍历链表,方法1
    public void loop1(Consumer<Integer> consumer) {
        Node p = head; // 头节点赋给p
        while (p != null) {
            consumer.accept(p.value);
            p = p.next;
        }
    }
    // 循环遍历,方法2
    public void loop2(Consumer<Integer> consumer) {
        for(Node p = head; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }
    
    // 遍历,方法3,使用迭代器,实现接口Iterable
    public class NodeIterator implements Iterator<Integer> {
        Node p = head;
        
        @Override
        public boolean hasNext() {  //判断是否有下一个元素
            return p != null;
        }

        @Override
        public Integer next() {     //返回当前节点值
            int value = p.value;
            p = p.next;
            return value;      
        }
    }
}

测试

public class TestSinglyLinkedList() {
	
    // 尾部添加, 根据索引查找对应节点值测试
    @Test
    public void test3() {
        SinglyLinkedList list = new SinglyLinkedList();
        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
        
        // Assertions.assertIterableEquals(List.of(1, 2, 3, 4), list);
        
        int i = list.get(2);
        System.out.println(i);  //i = 3
        
    }
    
     @Test
    public void test4() {
        SinglyLinkedList list = new SinglyLinkedList();
        list.addLast(1);
        list.addLast(2);
        list.addLast(3);
        list.addLast(4);
       
		list.insert(2, 5); 向索引为2的位置插入元素5
        for(Integer value : list) {
            System.out.print(value);
        }
        
    }
}

带哨兵的单向链表

// 单项链表
public class SinglyLinkedList implements Iterable<Integer>() {
    
    private Node head = new Node(666, null);  // 头指针
    
    // 节点类
    private static class Node() {
        int value;  // 值
        Node node;  // 下一个节点
        
        public Node(int value, Node node) {
            this.value = value;
            this.node = node;
        }
    }
    
    @Override
    public Iterator<Integer> iterator() {
        // 匿名内部类 -> 带名字的内部类
        return new NodeIterator();
    }
    
    // 1.链表头部添加元素
    public void addFirst(int value) {
		insert(0, value);
    }
    
    // 寻找最后一个节点
    private Node findLast() {
        Node p;
        for(p = head; p.next != null; p = p.next) {
            
        }
        return p;
    }
    
    // 3.尾部添加元素
    private void addLast(int value) {
        Node last = findLast();
        last.next = new Node(value, null);
    }
    
    // 4.根据索引值查找结点
    public Node findNode(int index) {
        int i = -1;
        for(Node p = head; p != null; p = p.next, i++) {
            if (index == i) {
                return p;
            }
        }
        // 未找到
        return null;
    }
    
    // 5.根据index找到对应节点,并返回节点值
    public int get(int index) {
        Node p = findNode(index);
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        return p.value;
    }
    
    // 6.向索引位置插入元素
    public void insert(int index, int value) {
        // 1.找到插入索引位置的上一个节点
        Node p = findNode(index - 1);
        // 判断p节点是否为空
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node n = p.next;
        // p存在的情况
        p.next = new Node(value, n);
    }
    
    // 7.删除头部节点
    public void removeFirst() {
		remove(0);
    }
    
    // 8.按索引位置删除节点
    public void remove (int index) {
        Node p = findNode(index - 1);  // 找到待删除节点的上一个节点
        if (p == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node n = p.next;               // 待删除节点
        if (n == null) {
            // 未找到待删除节点
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        p.next = n.next;			   // 待删除节点的上一个节点p的next指向待删除结点的下一个节点
    }
    
    // 6.遍历链表,方法1
    public void loop1(Consumer<Integer> consumer) {
        Node p = head.next; // 头节点赋给p
        while (p != null) {
            consumer.accept(p.value);
            p = p.next;
        }
    }
    // 循环遍历,方法2
    public void loop2(Consumer<Integer> consumer) {
        for(Node p = head.next; p != null; p = p.next) {
            consumer.accept(p.value);
        }
    }
    
    // 遍历,方法3,使用迭代器,实现接口Iterable
    public class NodeIterator implements Iterator<Integer> {
        Node p = head.next;
        
        @Override
        public boolean hasNext() {  //判断是否有下一个元素
            return p != null;
        }

        @Override
        public Integer next() {     //返回当前节点值
            int value = p.value;
            p = p.next;
            return value;      
        }
    }
}

双向链表–哨兵

public class DoublyLinkedListSentinel implements Iterator<Integer> {
     
    static class Node {
        Node prev;		// 上一节点
        int value;		// 值
        Node next;		// 下一节点
        
        public Node (Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
    
    private Node head; //头哨兵节点
    private Node tail; //尾哨兵节点
    
    public DoublyLinkedListSentinel () {
        head = new Node(null, 666, null);
        tail = new Node(null, 888, null);
        head.next = tail;
        tail.prev = head;
    }
    
    // 根据索引位置找节点
    private Node findIndex(int index) {
        int i = -1;
        for(Node p = head; p != tail; p = p.next, i++) {
        	if (index == i) {
                return p;
            }    
        }
        return null;
    }
    
    // 头部插入元素
    public void addFirst(int value) {
        insert(0, value);
    } 
    // 1.对应索引位置插入元素
    public void insert(int index, int value) {
        Node prev = findIndex(index - 1);		// 插入索引位置前一个结点
        if (prev == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node next = prev.next;					// 成为待插入元素节点的下一个节点
        Node node = new Node(prev, value, next); 
        prev.next = node;
        next.prev = node;
    }
    
    // 尾部添加元素
    public void addLast(int value) {
        Node last = tail.prev;
        Node added = new Node(last, value, tail);
        last.next = added;
        tail.prev = added; 
    }
    
    // 删除最后一个节点
    public void removeLast() {
        Node removed = tail.prev;
        if (removed == head) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node prev = removed.prev;
        prev.next = tail;
        tail.prev = prev;
    }
    
    // 删除头节点
    public void removeFirst() {
        remove(0);
    }
    
    // 删除节点
    public void remove(int index) {
        Node prev = findIndex(index - 1);		// 删除索引位置前一个结点
        if (prev == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node next = prev.next.next;					// 待删除元素节点的下一个节点
        if (next == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
		prev.next = next;
        next.prev = prev;
    }
    
    // 迭代器遍历
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node p = head.next;
            @Override
            public boolean hasNext() {
                return p != tail;
            }
            
            @Override 
            public Integer next() {
                int value = p.value;
                p = p.next;
                return value;
            }
        }
    }
    
}

环形链表

双向环形链表带哨兵,哨兵既作为头哨兵,又作为尾哨兵
在这里插入图片描述

public class DoublyLinkedListSentinel {
     
    private static class Node {
        Node prev;		// 上一节点
        int value;		// 值
        Node next;		// 下一节点
        
        public Node (Node prev, int value, Node next) {
            this.prev = prev;
            this.value = value;
            this.next = next;
        }
    }
    
    private Node sentinel = new Node(null, -1, null);
    
    public DoublyLinkedListSentinel () {
        sentinel.prev = sentinel;
        sentinel.next = sentinel;
    }
    
    // 根据索引位置找节点
    private Node findIndex(int index) {
        int i = -1;
        for(Node p = sentinel; p != sentinel; p = p.next, i++) {
        	if (index == i) {
                return p;
            }    
        }
        return null;
    }
    
    // 头部插入元素
    public void addFirst(int value) {
        Node a = sentinel;
        Node b = sentinel.next;
        Node added = new Node(a, value, b);
        a.next = added;
        b.prev = added;
    } 
    
    // 1.对应索引位置插入元素
    public void insert(int index, int value) {
        Node prev = findIndex(index - 1);		// 插入索引位置前一个结点
        if (prev == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node next = prev.next;					// 成为待插入元素节点的下一个节点
        Node node = new Node(prev, value, next); 
        prev.next = node;
        next.prev = node;
    }
    
    // 尾部添加元素
    public void addLast(int value) {
        Node b = sentinel;
        Node a = sentinel.prev;
        Node added = new Node(a, value, b);
        b.prev = added;
        a.next = added;
    }
    
    // 删除头节点
    public void removeFirst() {
        Node removed = sentinel.next;
        if (removed == sentinel) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node a = sentinel;
        Node b = removed.next;
        a.next = b;
        b.prev = a;
    }
    
    // 删除最后一个节点
    public void removeLast() {
        Node removed = sentinel.prev;
        if (removed == sentinel) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node a = sentinel;
        Node b = removed.prev;
        a.prev = b;
        b.next = a;
    }
    
    // 根据节点值查找该节点
    public Node findByValue(int value) {
        Node p = sentinel.next;
        whlie (p != sentinel) {
            if (p.value == value) {
                return p;
            }
            p = p.next;
        }
        return null;
    }
    
    // 根据节点值删除节点
    public void remove(int vlaue) {
        Node removed = findByValue(value);
        if (removed == null) {
            // 节点为空,抛出异常
            threw new IllegalArgumentException(String.format("index [%d]不合法%n", index));
        }
        Node a = removed.prev;
        Node b = removed.next;
        a.next = b;
        b.prev = a;
    }
    
    // 迭代器遍历
    @Override
    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            Node p = sentinel.next;
            @Override
            public boolean hasNext() {
                return p != sentinel;
            }
            
            @Override 
            public Integer next() {
                int value = p.value;
                p = p.next;
                return value;
            }
        }
    }
}
  • 29
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值