Java数据结构:链表

1、链表定义:

链表通过指针将不连续的内存块串起来,构成的一种数据结构。单向链表、双向链表,双端链表、单向循环、双向循环、跳跃链表等原理也都是基于指针将不连续的内存快串起来。只是指针指向复杂度不一样,提供的各种的API而已。

/**
* 单链表的表示方式:
*/
class Node {
    Object element; //节点数据信息:数据域
    Node next;  //下一个节点的地址(引用)
}

2、链表种类:

单向链表:

 private class Node {
    // 保存节点的数据
    private Object element;
    // 指向下一个节点的引用
    private Node next;
}

单向循环链表:单向循环链表无非就是最后一个节点的指针指向第一个节点

双向链表、双向循环链表

 private class Node {
    // 保存节点的数据
    private Object data;
    // 保存上个节点的引用
    private Node prev;
    // 指向下一个节点的引用
    private Node next;
}

 

3、单向链表相关方法java实现:

@Data
class Node<T> {
    public T data;
    public Node next;
    //next表示:下一个节点对象(单链表中)
    public Node(T data) {
        this.data = data;
    }
}

 

/**
*单向链表java实现类
*/
public class MyLinkList<T>{

    /**
     * 当前结点对象
     */
    Node head;
    /**
     * 当前结点对象
     */
    Node current;

    /**
     * 插入指定位置元素
     *
     * @param index
     * @param obj
     */
    public void add(int index, T obj) {
        Node<T> node = head;
        for (int i = 1; i < index; i++) {
            node = node.next;
        }
        Node newNode = new Node(obj);
        Node temp = node.next;
        node.next = newNode;
        newNode.next = temp;
    }

    /**
     * 在链表末端插入元素
     *
     * @param obj
     */
    public void add(T obj) {
        if (head == null) {
            Node newNode = new Node(obj);
            current = head = newNode;
            return;
        }
        if (current.next != null) {
            current = current.next;
            add(obj);
            return;
        }
        Node newNode = new Node(obj);
        current.next = newNode;
    }


    /**
     * 根据下标删除元素
     *
     * @param index
     */
    public void remove(int index) {
        Node<T> node = head;
        for (int i = 0; i < index - 1; i++) {
            node = node.next;
        }
        node.next = node.next.next;
    }


    /**
     * 根据特定元素删除(删除第一个)
     *
     * @param data
     */
    public void remove(T data) {
        if (head.next == null) {
            return;
        }
        if (head.next.data.equals(data)) {
            head.next = head.next.next;
        } else {
            remove(data);
        }

    }

    /**
     * 获取指定位置的元素
     *
     * @param index
     * @return
     */
    public T get(int index) {
        Node<T> node = head;
        for (int i = 0; i <= index - 1; i++) {
            node = node.next;
        }
        return node.data;
    }

    /**
     * 打印链表
     */
    public void print() {
        String str = "";
        Node<T> node = head;
        while (node.next != null) {
            str += node.data + "->";
            node = node.next;
        }
        str += node.data;
        System.err.println(str);
    }
}

4、链表的时间复杂度分析:

链表是通过指针将不连续的内存块串起来。链表的单纯插入和删除很快,我们只需要改变一些指针的指向就行了 ,所以就插入和删除时间复杂度为O(1),但是在删除元素之前需要先查找到元素。

通过下标和特定元素查询数据,都需要从链表的第一个或最后一个数据项开始查询。查询的最好时间复杂度为O(1)(元素在链表头),查询的最坏时间复杂度为O(n)(元素在链表尾)。根据时间复杂度加法原则,链表的插入和删除的时间复杂度为O(n)。

5、链表的练习:

常见链表问题:(这里只提供思路,就不写代码了)

(1)链表中环的检测:

利用快、慢指针解法。慢指针,一次遍历一个节点。快指针,一次遍历两个节点。如果在某一次遍历中相遇。则检测到环

(2)删除链表倒数第 n 个结点:

利用快、慢指针解法。让快指针先走N步,再让两个在指针同时后移,直到快指针到达尾部,此时,慢指针的下一个节点就是要被删除的节点了。

(3)求链表的中间结点

利用快、慢指针解法。慢指针一次遍历一个节点,快指针一次遍历两个节点,由于快指针的速度是慢指针的两倍,所以当快指针遍历完链表时,慢指针所处的节点就是链表的中间节点。

(4)单链表反转:

public Node reverse(Node head) {
    if (head == null || head.next == null)
        return head;
    Node temp = head.next;
    Node newHead = reverse(head.next);
    temp.next = head;
    head.next = null;
    return newHead;
}

(5)单链表反向打印:(不改变链表结构)

从头到尾打印链表,所谓的“后进先出”,这时候就可以联想到栈这个数据结构。但是会开辟新的内存空间。想到栈就要想到递归

6、链表的应用

  • 可以基于链表实现栈、队列、树
  • Redis的数据类型Sorted Set(有序集合),底层编码实现ziplist和skiplist,skiplist底层就是跳跃链表+hash表
  • java并发容器:AbstractQueuedSynchronizer 底层。当前线程调用lock()方法是获取独占式锁,获取失败就将当前线程加入同步队列。此时的同步队列为双向队列

 /* <pre>
 *      +------+  prev +-----+       +-----+
 * head |      | <---- |     | <---- |     |  tail
 *      +------+       +-----+       +-----+
 * </pre>
 */
static final class Node {
       
	static final Node SHARED = new Node();
 
	static final Node EXCLUSIVE = null;
 
	static final int CANCELLED =  1;
  
	static final int SIGNAL    = -1;
  
	static final int CONDITION = -2;
  
	static final int PROPAGATE = -3;
	
	volatile int waitStatus;

	volatile Node prev;
	
	volatile Node next;
   
	volatile Thread thread;
   
	Node nextWaiter;

   //省略部分代码
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值