前面翘的链表是单向的,也就是只能从一头开始遍历和操作,每个节点用next关联到下一个节点,今天把双向的敲了一下,双向的就多了一个previous,用它来关联它的上一个节点,这样,链表可以实现从前面操作,也可以从后面操作,单向的链表也就是只有一个头,双向的链表就是两个头,从哪个头都可以对连彼岸进行操作,所谓的操作也就是插入,删除,遍历,打印,等等等等
下面是代码,还是老样子,先是节点类登场,和单向的节点类唯一的区别就是多了一个用来关联前面节 点 的节点。
public Link previous; //定义指向前一个节点
具体代码如下:
package 双向链表; /* * 节点类,封装了属性 * @author TMS * */ public class Link { public long data; public Link next; //定义指向下一个节点 public Link previous; //定义指向前一个节点 /* * 初始化节点的属性 */ public Link(long data) { this.data = data; } /* * 打印出每个节点的信息 */ public void displayLink() { System.out.println("data = "+data); } }
接下来是双向链表的类:里面主要的方法有,
public boolean isEmpty() //判断链表是否为空 public void insertFirst(long value) //从前面插入 public void insertLast(long value) //从后面插入 public Link deleteFirst() //删除前面头的节点 public Link deleteLast() //删除后面头的节点 public boolean insertAftre(long key, long value) //插入到指定节点的后面 public Link deletekey(long key) //删除指定的节点 public void displayFd() //从前面遍历打印 public void displayBd() //从后面遍历打印
下面是具体的代码实现:
package 双向链表; public class DoubleLink { private Link first; private Link last; public DoubleLink() { first = null; last = null; } public boolean isEmpty() { return first == null; } /* * 在头节点插入 */ public void insertFirst(long value) { Link newLink = new Link(value); if (isEmpty()) { //进行判断如果为空,last为newLink,因为这是从前面插入,所以要求是last为newLink last = newLink; } else first.previous = newLink; //不为空的时候,first前'指针'指向新节点 newLink.next = first; //新节点指向原来的头 first = newLink; //从新将头放到最前面 } /* * 在尾节点插入 */ public void insertLast(long value) { Link newLink = new Link(value); if (isEmpty()) { first = newLink; } else { last.next = newLink; newLink.previous = last; } last = newLink; // / } /* * 删除头节点 */ public Link deleteFirst() { Link temp = first; if (first.next == null) { //如果只有一个节点 last = null; //和上面的从头插入一样,让他的尾节点为空。(这种情况是头和尾在一起) } else { first.next.previous = null; //其他情况下,让头节点的下一个节点的前面的节点为空,也就是原来的头节点为空 first = first.next; //将头节点移到下一个节点处 } return temp; //返回删除的 } /* * 删除节点 */ public Link deleteLast() { Link temp = last; if (first.next == null) { first = null; } else { last.previous.next = null; last = last.previous; } return temp; } /* * 在指定的节点后面插入一个新的节点,key值是原来链表中就有的,用来定位,value是新插入节点的值 */ public boolean insertAftre(long key, long value) { Link current = first; while (current.data != key) { //遍历到链表中相应的key处。 current = current.next; if (current.next == null) { return false; // 没找到 } } Link newLink = new Link(value); //初始化新的节点 if (current == last) { //如果到key是最后的尾节点 newLink.next = null; //新节点的next为空 last = newLink; //last定位到最后新插入的节点处 } else { //key在头或中间的时候 newLink.next = current.next; //新节点的next等于key对应的当前节点(current)的next current.next.previous = newLink; //当前key对应的节点的next节点的previous(前节点)指向新的节点 } newLink.previous = current; //通过上面的一连串之后,最后将新节点的previous指向key对应的当前节点(current) current.next = newLink; //当前节点(key对应)的next指向新的节点 return true; } /* * 删除指定 值的节点 key为节点对应的值 */ public Link deletekey(long key) { Link current = first; while (current.data != key) { //遍历链表锁定到key对应的节点处 current = current.next; if (current == null) { //到最后了都没有找到相应的节点 return null; // 没找到返回null } } if (current == first) { //经过遍历如果在第一个节点处 first = first.next; //将头节点移到下一个节点处 } else { //其他的情况下,当前节点的上一个节点的下一个节点 指向当前的节点的下一个节点 current.previous.next = current.next; } if (current == last) { //经过遍历如果在尾节点处 last = current.previous; //直接将尾节点移动到当亲节点的前一个节点处 } else { //其他情况下,也就是key对应的节点在链表中间的时候 current.next.previous = current.previous; //当前节点的下一个节点的前一个节点指向当前节点的前一个节点,跳过了当前节点 } return current; } /* * 从头节点开始打印链表的数据 */ public void displayFd() { System.out.println("从头往后面打印节点"); Link current = first; while (current != null) { //从头开始遍历 current.displayLink(); current = current.next; } } /* * 从尾节点开始打印链表的数据 */ public void displayBd() { System.out.println("从后往前打印节点"); Link current = last; while(current != null) { //从尾节点开始遍历 current.displayLink(); current = current.previous; } } /* * 测试方法 */ public static void main(String[] args) { DoubleLink dl = new DoubleLink(); //从前面插入节点 dl.insertFirst(1); dl.insertFirst(2); dl.insertFirst(3); //从前面打印节点 dl.displayFd(); //从后面插入节点 dl.insertLast(4); dl.insertLast(5); dl.insertLast(6); //从前面和后面打印节点 dl.displayFd(); dl.displayBd(); System.out.println("删除节点:"); //从前面和后面打印节点 dl.deleteFirst(); dl.deleteLast(); dl.displayFd(); dl.displayBd(); //在链表中找到相应的值再在后面插入节点 dl.insertAftre(1, 1222); dl.displayFd(); } }
PS:
以上的方法基本包括了链表操作的所有的方法,只是基本,我暂时不知道链表还能怎么玩,一天一天的 写, 最大的感受就是自己越来越爱写注释了,希望把每一步都能写清楚,这应该也是一点进步哈。
洗洗睡觉,又那么晚了⊙﹏⊙。
相关推荐
在算法中,双向链表常用于实现LRU缓存淘汰策略、表达式求值等场景。 "赛程问题"通常指的是调度或优化问题,可能涉及到贪心算法或优先队列。这类问题可能需要确定最优的比赛顺序,以满足某些条件,比如最大化收益或...
- **链表**:包括单链表、双向链表和循环链表,以及它们的插入、删除操作。 - **树**:如二叉树、平衡树(AVL树、红黑树)、B树、B+树等,用于数据存储和检索。 - **图**:包括邻接矩阵和邻接表两种表示方式,...
LinkedList基于双向链表,插入和删除速度快,但随机访问速度慢。 4. **HashMap的实现原理**:HashMap使用哈希表存储键值对,通过键的哈希函数快速定位元素,实现快速查找。它内部使用了Entry数组,当哈希冲突时采用...
\数据结构flash演示\版本1\2-3-6双向链表的插入.swf \数据结构flash演示\版本1\2-3-7双向链表的删除.swf \数据结构flash演示\版本1\2-3-8.swf \数据结构flash演示\版本1\2-3-9两个有序链表的连接.swf \数据结构...
- **实现**:可以通过双向链表和哈希表结合的方式实现。 #### 4. 进程间通信(IPC) - **同一父进程中的子进程**:共享内存、管道。 - **不同父进程中的子进程**:消息队列、共享内存、套接字。 - **父进程与子...