服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - java数据结构基础:单,双向链表

java数据结构基础:单,双向链表

2021-10-25 13:10鱼小洲 Java教程

这篇文章主要介绍了Java的数据解构基础,希望对广大的程序爱好者有所帮助,同时祝大家有一个好成绩,需要的朋友可以参考下,希望能给你带来帮助

单向链表

单向链表比顺序结构的线性表最大的好处就是不用保证存放的位置,它只需要用指针去指向下一个元素就能搞定。

单链表图解

java数据结构基础:单,双向链表

图画的比较粗糙,简单的讲解一下:

上面四个长方形,每个长方形都是一个节点。在长方形中,一种包含两个东西,一个是当前节点的元素,一个是指向下一节点的地址。这个下一个节点的地址指向了下一个节点中的元素。以此类推。

在最左边的叫做头节点,同样,最后面的叫尾节点。

所以,我们所有的操作都是根据节点来进行操作。

代码

这些代码都有很详细的注释,我就不做过多的解释了,你直接复制代码到本地idea运行一遍就全部知道了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
package com.zxy.lianbiao;
/**
 * @Author Zxy
 * @Date 2021/2/3 21:25
 * @Version 1.0
 */
/**
 * 基于单向链表实现元素的存取
 *
 * @param <E>
 */
public class MySinglyLinkedList<E> implements MyList<E> {
    /**
     * 定义单向链表中的节点对象
     */
    class Node<E> {
        private E item; // 存储元素
        private Node next; // 存储下一个节点对象
        public Node(E item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
    private Node head; // 存放链表中的头节点
    private int size; // 记录元素的个数
    /**
     * 向链表中添加元素
     *
     * @param element
     */
    @Override
    public void add(E element) {
        // 创建节点
        Node<E> node = new Node<>(element, null);
        // 找到尾节点
        Node tail = getTail();
        // 节点的挂接
        if (tail == null) { // 如果没有尾节点,那意思就是头节点都不存在
            // 没有头节点,那么就把创建的节点给头节点
            this.head = node;
        } else {
            tail.next = node;
        }
        // 记录元素的个数
        this.size++;
    }
    /**
     * 找尾节点
     */
    private Node getTail() {
        // 判断头节点是否存在
        if (this.head == null) {
            return null;
        }
        // 查找尾节点
        Node node = this.head;
        while (true) {
            if (node.next == null) {
                break;
            }
            node = node.next; // 移动指针指向下一个
        }
        return node;
    }
    /**
     * 根据元素的位置获取元素
     *
     * @param index
     * @return
     */
    @Override
    public E get(int index) {
        // 校验index的合法性
        this.checkIndex(index);
        // 根据位置获取指定节点
        Node<E> node = this.getNode(index);
        // 将该节点中的元素返回
        return node.item;
    }
    /**
     * 对index进行校验
     */
    private void checkIndex(int index) {
        // 0<=index<size
        if (!(index >= 0 && index < this.size)) {
            throw new IndexOutOfBoundsException("Index: " + index + "   this.size: " + this.size);
        }
    }
    /**
     * 根据位置获取节点
     */
    private Node<E> getNode(int index) {
        Node<E> node = this.head;
        for (int i = 0; i < index; i++) {
            node = node.next;
        }
        return node;
    }
    /**
     * 获取元素的个数
     *
     * @return
     */
    @Override
    public int size() {
        return this.size;
    }
    /**
     * 根据元素位置删除元素
     *
     * @param index
     * @return
     */
    @Override
    public E remove(int index) {
        // 校验index合法性
        this.checkIndex(index);
        // 根据位置找到节点对象
        Node<E> node = getNode(index);
        // 获取该节点对象中的元素
        E item = node.item;
        // 将该节点对象从单向链表中移除
        // 判断当前删除的节点是否为头节点
        if (this.head == node) {
            this.head = node.next;
        } else {
            Node<E> temp = this.head;
            for (int i = 0; i < index - 1; i++) {
                temp = temp.next; // 此时的temp就是要删除的那个节点的前一个节点
            }
            temp.next = node.next; // 将当前节点的前一个节点指向当前节点的后一个节点
        }
        // 然后将当前节点的下一个节点指向null
        node.next = null;
        // 记录元素个数
        this.size--;
        // 将该元素返回
        return item;
    }
    /**
     * 插入节点思路:如果当前共有三个节点分别是1,2,3,在1和2的中间插入4,原本的指向是1->2 现改变成1->4 4->2 先获取到指定位置的node,再获取到前一个位置的node和下一个位置的node
     */
    public void insert(int index, E element) {
        // 先根据要插入的位置拿到这个位置的节点对象
        Node<E> item = getNode(index);
        // 根据插入的元素新建一个节点
        Node<E> eNode = new Node<>(element, null);
        // 如果是头节点,那么就找不到前一个,直接把这个赋值给head
        if (index == 0){
            // index==0代表是替换掉头节点
            this.head = eNode;
            eNode.next = item;
            this.size++;
        }else {
            // 根据当前的节点对象去找到前一个节点对象和后一个节点对象
            Node<E> before = this.head; // 根据头节点去找
            for (int i = 0; i < index - 1; i++) {
                before = before.next; // 此时的before就是当前节点的前一个节点
            }
            before.next = eNode;
            eNode.next = item;
            this.size++;
        }
    }
    public static void main(String[] args) {
        MySinglyLinkedList<String> list = new MySinglyLinkedList<>();
        System.out.println("添加节点开始------------------------");
        list.add((String) "a");
        list.add((String) "b");
        list.add((String) "c");
        list.add((String) "d");
        System.out.println("添加节点完成-------------------------\n");
        System.out.println("插入指定的元素");
        list.insert(0,"f");
        for (int i = 0; i < list.size; i++) {
            System.out.println(list.get(i));
        }
    }
}

双向链表

昨天写完单向链表和栈结构之后,看了看程杰大大的书中有介绍双向链表的部分。虽然是c语言写的,但是我还是用Java给翻译出来了。

思路如下:

首先,双向链表和单向链表的最大区别就是,双向链表比单链表多了个指向前一节点的指针。代码量其实并不比单链表多很多,只是思路的转变需要克服一下。

其次就是在插入元素的时候,我们可以在链表的头部插入,也可以在链表的尾部插入(因为有两个指针嘛)

编码

代码其实和单链表差不多,如果感兴趣的话可以去看看我之前写的单链表的文章。虽然文笔很烂,但是代码货真价实。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package com.zxy.lianbiao;
/**
 * @Author Zxy
 * @Date 2021/2/4 20:11
 * @Version 1.0
 */
/**
 * 基于双向链表实现元素存取的容器
 *
 * @param <E>
 */
public class MyDoublyLinkedList<E> implements MyList<E> {
 
    /**
     * 定义双向链表节点对象
     */
    class Node<E> {
        E item; // 记录元素
        Node<E> prev; // 记录前一个节点对象
        Node<E> next; // 记录下一个节点对象
        public Node(Node<E> prev, E item, Node<E> next) {
            this.item = item;
            this.prev = prev;
            this.next = next;
        }
    }
    private Node head; // 记录头节点
    private Node tail; // 记录尾节点
    private int size; // 记录元素个数
    /**
     * 向双向链表中添加元素的方法
     *
     * @param element
     */
    @Override
    public void add(E element) {
        linkLast(element);
    }
    /**
     * 将节点对象添加到双向链表的尾部
     */
    private void linkLast(E element) {
        Node t = this.tail; // 获取尾节点
        Node<E> node = new Node<>(t, element, null); // 创建节点对象
        this.tail = node; // 将新节点定义为尾节点 因为原来的尾节点被这个新节点替代了
        if (t == null) {
            // 说明一个节点都没有,这个还得是头节点
            this.head = node;
        } else {
            t.next = node;
        }
        this.size++;
    }
    /**
     * 根据指定位置获取元素
     *
     * @param index
     * @return
     */
    @Override
    public E get(int index) {
        this.checkIndex(index);
        // 根据位置查找节点对象
        Node<E> node = this.getNode(index);
        return node.item;
    }
    /**
     * 对index的合法性校验
     */
    private void checkIndex(int index) {
        if (!(index >= 0 && index < this.size)) {
            throw new IndexOutOfBoundsException();
        }
    }
    /**
     * 根据位置获取指定节点对象
     */
    private Node getNode(int index) {
        // 判断当前位置距离头或者尾哪个节点更近  使用二分法
        if (index < (this.size >> 1)) {
            Node node = this.head;
            for (int i = 0; i < index; i++) {
                node = node.next;
            }
            return node;
        } else {
            Node node = this.tail;
            for (int i = this.size - 1; i > index; i--) {
                node = node.prev;
            }
            return node;
        }
    }
    /**
     * 返回元素的个数
     *
     * @return
     */
    @Override
    public int size() {
        return this.size;
    }
    /**
     * 删除元素
     *
     * @param index
     * @return
     */
    @Override
    public E remove(int index) {
        // 对index进行合法性校验
        this.checkIndex(index);
        Node node = this.getNode(index); // 根据位置获取到节点对象
        // 获取节点对象的元素
        E item = (E) node.item;
        // 判断当前节点是否为头节点
        if (node.prev == null) {
            this.head = node.next;
        } else {
            node.prev.next = node.next;
        }
        // 判断当前节点是否为尾节点
        if (node.next == null) {
            // node.prev.next = null;
            this.tail = node.prev;
        } else {
            node.next.prev = node.prev;
        }
        // 当前节点断掉与他后继节点的连接
        node.next = null;
        // 当前节点断掉与直接前驱节点的连接
        node.prev = null;
        node.item = null;
        this.size--;
        return item;
    }
    /**
     * 在双向链表的头添加元素
     */
    public void addFirst(E element) {
        this.linkFirst(element);
    }
    /**
     * 在链表的头添加元素
     *
     * @param element
     */
    public void linkFirst(E element) {
        // 获取头节点对象
        Node head = this.head;
        Node<E> eNode = new Node<>(null, element, head);
        // 将新节点定义为头节点
        this.head = eNode;
        if (head == null) {
            // 如果为空,说明该链表中一个节点都没有 也就是该头节点也是尾节点
            this.tail = eNode;
        } else {
            head.prev = eNode;
        }
        this.size++;
    }
    /**
     * 在链表的尾部添加元素
     *
     * @param element
     */
    public void addLast(E element) {
        this.linkLast(element);
    }
    public static void main(String[] args) {
        MyDoublyLinkedList<String> list = new MyDoublyLinkedList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("e");
        System.out.println(list.remove(2));
        System.out.println(list.size);
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
    }
}

总结

本篇文章就到这里了,希望能给你带来帮助,也希望您能够多多关注服务器之家的更多内容!

原文链接:https://blog.csdn.net/weixin_43581288/article/details/113660067

延伸 · 阅读

精彩推荐
  • Java教程20个非常实用的Java程序代码片段

    20个非常实用的Java程序代码片段

    这篇文章主要为大家分享了20个非常实用的Java程序片段,对java开发项目有所帮助,感兴趣的小伙伴们可以参考一下 ...

    lijiao5352020-04-06
  • Java教程Java使用SAX解析xml的示例

    Java使用SAX解析xml的示例

    这篇文章主要介绍了Java使用SAX解析xml的示例,帮助大家更好的理解和学习使用Java,感兴趣的朋友可以了解下...

    大行者10067412021-08-30
  • Java教程Java8中Stream使用的一个注意事项

    Java8中Stream使用的一个注意事项

    最近在工作中发现了对于集合操作转换的神器,java8新特性 stream,但在使用中遇到了一个非常重要的注意点,所以这篇文章主要给大家介绍了关于Java8中S...

    阿杜7472021-02-04
  • Java教程小米推送Java代码

    小米推送Java代码

    今天小编就为大家分享一篇关于小米推送Java代码,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧...

    富贵稳中求8032021-07-12
  • Java教程xml与Java对象的转换详解

    xml与Java对象的转换详解

    这篇文章主要介绍了xml与Java对象的转换详解的相关资料,需要的朋友可以参考下...

    Java教程网2942020-09-17
  • Java教程升级IDEA后Lombok不能使用的解决方法

    升级IDEA后Lombok不能使用的解决方法

    最近看到提示IDEA提示升级,寻思已经有好久没有升过级了。升级完毕重启之后,突然发现好多错误,本文就来介绍一下如何解决,感兴趣的可以了解一下...

    程序猿DD9332021-10-08
  • Java教程Java实现抢红包功能

    Java实现抢红包功能

    这篇文章主要为大家详细介绍了Java实现抢红包功能,采用多线程模拟多人同时抢红包,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙...

    littleschemer13532021-05-16
  • Java教程Java BufferWriter写文件写不进去或缺失数据的解决

    Java BufferWriter写文件写不进去或缺失数据的解决

    这篇文章主要介绍了Java BufferWriter写文件写不进去或缺失数据的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望...

    spcoder14552021-10-18