双向链表:
双向链表和单链表的区别:
单链表只有一个指向下一个元素的引用next,而双向链表还多一个指向前一个元素的引用pre。在单链表中如果想操作下一个元素必须都得找到下一个元素的上一个元素,用上一个元素来操作。而双链表则不需要。
定义节点类:
//定义双向链表的节点类
class DoubleLinkedNode{
//数据域
private Integer no;
private String name;
//指针域,两个
private DoubleLinkedNode pre;
private DoubleLinkedNode next;
public DoubleLinkedNode() {
}
public DoubleLinkedNode(Integer no) {
this.no = no;
}
public DoubleLinkedNode(Integer no, String name) {
this.no = no;
this.name = name;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DoubleLinkedNode getPre() {
return pre;
}
public void setPre(DoubleLinkedNode pre) {
this.pre = pre;
}
public DoubleLinkedNode getNext() {
return next;
}
public void setNext(DoubleLinkedNode next) {
this.next = next;
}
@Override
public String toString() {
return "DoubleLinkedNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
一、双向链表的头插法:
原理示意图:
package com.fan.linkedlist;
public class DoubLinkedListTest3 {
public static void main(String[] args) {
DoubLinkedList3 doubLinkedList3 = new DoubLinkedList3();
doubLinkedList3.addFirst(1,"张1");
doubLinkedList3.addFirst(2,"张2");
doubLinkedList3.addFirst(3,"张3");
doubLinkedList3.addFirst(4,"张4");
System.out.println("遍历链表:");
doubLinkedList3.showFirst();
}
}
class DoubLinkedList3{
DoubleLinkedNode headNode = new DoubleLinkedNode();
DoubleLinkedNode tailNode = new DoubleLinkedNode();
public DoubLinkedList3() {
headNode.setNext(null);
tailNode.setNext(null);
headNode = tailNode = null;//设置为空方便遍历
}
//增加节点的方法
public void addFirst(Integer v,String name){
DoubleLinkedNode newNode = new DoubleLinkedNode(v,name);
if(headNode == null){//空链表的时候添加
headNode = newNode;
}else{
newNode.setNext(headNode);
headNode.setPre(newNode);
headNode = newNode;
}
}
//链表的遍历:正向遍历
public void showFirst(){
//我们一般从有效节点开始遍历
DoubleLinkedNode curr = headNode;//headNode是一个指针,用于遍历
while(true){
if(curr == null){
break;
}
System.out.print(curr+"-->");//打印当前节点信息
curr = curr.getNext();//临时节点后移
}
}
}
//定义双向链表的节点类
class DoubleLinkedNode{
//数据域
private Integer no;
private String name;
//指针域,两个
private DoubleLinkedNode pre;
private DoubleLinkedNode next;
public DoubleLinkedNode() {
}
public DoubleLinkedNode(Integer no, String name) {
this.no = no;
this.name = name;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DoubleLinkedNode getPre() {
return pre;
}
public void setPre(DoubleLinkedNode pre) {
this.pre = pre;
}
public DoubleLinkedNode getNext() {
return next;
}
public void setNext(DoubleLinkedNode next) {
this.next = next;
}
@Override
public String toString() {
return "DoubleLinkedNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
测试结果:
代码二:模仿linkedList代码写的:
package com.fan.applet;
//将来将测试类写在外面
public class DoubleLinkedList<E> {
//双向链表有头节点,尾节点,还有节点个数
private Node<E> first;//始终指向头节点
private Node<E> last;//始终指向尾节点
private Integer size = 0;//有效节点的个数
public DoubleLinkedList() {//无参构造
}
//头插法,思路,头节点移动,尾节点不动
public void addFirst(E e){
linkedFirst( e);
}
private void linkedFirst(E e){//将元素添加到链表头
Node<E> f = first;//让临时头节点指向头节点
Node<E> newNode = new Node<>(null,e,f);//设置新节点的后驱节点
first = newNode;//后驱节点设置好后,让头指着指向新头节点
if(f == null){
last = newNode;
}else{
f.pre = newNode;//设置原节点的前驱节点是 新节点
}
size++;
}
//尾插法,将新元素连接到链表末尾,头不动,尾节点移动
private void linkedLast(E e){
//声明一个临时的尾节点变量l 指向原尾节点
Node<E> l = last;
//将新元素包装成尾节点,并同时设置新节点的前驱节点是原尾节点l/last
Node<E> newNode = new Node<>(l,e,null);
//让尾节点指向新节点
last = newNode;
if(l == null){//如果原尾节点为null,即空链表
//则让头节点都指向新节点
first = newNode;
}else{
l.next = newNode;//不是空链表时,就要设置后驱节点,临时尾节点的下一个节点是新节点
}
size++;//节点数量加一
}
//末尾插入元素
public void addLast(E e){
linkedLast(e);
}
//链表的显示
public void show(){
Node<E> curr = first;
if(curr == null){
System.out.println("链表为空!!!");
}
while(true){
if(curr == null){
break;//到链表末尾
}
System.out.println(curr);
curr = curr.next;//指针后移
}
}
//使用内部类定义节点,对节点类进行了封装,也能访问到
private static class Node<E> {
private E e;
private Node<E> pre;
private Node<E> next;
//定义三参数的构造方法
public Node( Node<E> pre, E e,Node<E> next) {
this.pre = pre;
this.e = e;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"e=" + e +
'}';
}
}
}
测试:
package com.fan.applet;
public class DoubleLinkedListTest {
public static void main(String[] args) {
DoubleLinkedList doubleLinkedList = new DoubleLinkedList();
doubleLinkedList.addFirst(1);
doubleLinkedList.addFirst(2);
doubleLinkedList.addFirst(3);
doubleLinkedList.addFirst(4);
/*
doubleLinkedList.addLast(1);
doubleLinkedList.addLast(2);
doubleLinkedList.addLast(3);
doubleLinkedList.addLast(4);
* */
doubleLinkedList.show();
}
}
二、双向链表的尾插法:
尾插法代码,并演示尾插后正向遍历和反向遍历:
思路图和头插法类似:
package com.fan.linkedlist;
public class DoubLinkedListTest3 {
public static void main(String[] args) {
DoubLinkedList3 doubLinkedList3 = new DoubLinkedList3();
doubLinkedList3.addTail(1,"张1");
doubLinkedList3.addTail(2,"张2");
doubLinkedList3.addTail(3,"张3");
doubLinkedList3.addTail(4,"张4");
System.out.println("正向遍历链表:");
doubLinkedList3.showFirst();
System.out.println("\n反向遍历:");
doubLinkedList3.showTail();
//尾插法如何从头遍历
}
}
class DoubLinkedList3{
DoubleLinkedNode headNode = new DoubleLinkedNode();
DoubleLinkedNode tailNode = new DoubleLinkedNode();
public DoubLinkedList3() {
headNode.setNext(null);
tailNode.setNext(null);
headNode = tailNode = null;//设置为空方便遍历
}
//增加节点的方法,头插法
public void addFirst(Integer v,String name){
DoubleLinkedNode newNode = new DoubleLinkedNode(v,name);
if(headNode == null){//空链表的时候添加
headNode = newNode;//如果是空链表的话,直接添加
}else{
newNode.setNext(headNode);
headNode.setPre(newNode);
headNode = newNode;
}
}
public void addTail(Integer v,String name){
DoubleLinkedNode newNode = new DoubleLinkedNode(v,name);
if(tailNode == null){
tailNode = newNode;
headNode = newNode;//为了从头遍历,我们也让头节点指向第一个节点
}else{
newNode.setPre(tailNode);
tailNode.setNext(newNode);
tailNode = newNode;//尾指针后移
}
}
//链表的遍历:正向遍历
public void showFirst(){
//我们一般从有效节点开始遍历
DoubleLinkedNode curr = headNode;//headNode是一个指针,用于遍历
while(true){
if(curr == null){
break;
}
System.out.print(curr+"-->");//打印当前节点信息
curr = curr.getNext();//临时节点后移
}
}
//链表的遍历:反向遍历
public void showTail(){
//我们一般从有效节点开始遍历
DoubleLinkedNode curr = tailNode;//headNode是一个指针,用于遍历
while(true){
if(curr == null){
break;
}
System.out.print(curr+"-->");//打印当前节点信息
curr = curr.getPre();//临时节点后移
}
}
}
//定义双向链表的节点类
class DoubleLinkedNode{
//数据域
private Integer no;
private String name;
//指针域,两个
private DoubleLinkedNode pre;
private DoubleLinkedNode next;
public DoubleLinkedNode() {
}
public DoubleLinkedNode(Integer no, String name) {
this.no = no;
this.name = name;
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public DoubleLinkedNode getPre() {
return pre;
}
public void setPre(DoubleLinkedNode pre) {
this.pre = pre;
}
public DoubleLinkedNode getNext() {
return next;
}
public void setNext(DoubleLinkedNode next) {
this.next = next;
}
@Override
public String toString() {
return "DoubleLinkedNode{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
三、遍历链表节点:
class DoubLinkedList3{
DoubleLinkedNode headNode = new DoubleLinkedNode();
DoubleLinkedNode tailNode = new DoubleLinkedNode();
public DoubLinkedList3() {
headNode.setNext(null);
tailNode.setNext(null);
headNode = tailNode = null;//设置为空方便遍历
}
//链表的遍历:正向遍历
public void showFirst(){
//我们一般从有效节点开始遍历
DoubleLinkedNode curr = headNode;//headNode是一个指针,用于遍历
while(true){
if(curr == null){
break;
}
System.out.print(curr+"-->");//打印当前节点信息
curr = curr.getNext();//临时节点后移
}
}
//链表的遍历:反向遍历
public void showTail(){
//我们一般从有效节点开始遍历
DoubleLinkedNode curr = tailNode;//headNode是一个指针,用于遍历
while(true){
if(curr == null){
break;
}
System.out.print(curr+"-->");//打印当前节点信息
curr = curr.getPre();//临时节点后移
}
}
}
四、根据编号删除节点:
//根据编号删除
public void del(int no){
DoubleLinkedNode newNode = new DoubleLinkedNode(no);
DoubleLinkedNode curr = headNode;
boolean flag =false;
while(true){
if(curr == null ){
break;
}
if(curr.getNo() == newNode.getNo()){
flag = true;
break;
}
curr = curr.getNext();
}
if(flag){
if(curr.getNext() != null && curr.getPre() != null){
curr.getPre().setNext(curr.getNext());
curr.getNext().setPre(curr.getPre());
}
//如果是尾节点,则让当前节点的上一个节点指向null即可
if(curr.getNext() == null){
curr.getPre().setNext(null);
}
if(curr == headNode){
//如果删除的刚好是头节点,则让头节点指向curr的下一个节点作为新的头节点即可
headNode = curr.getNext();
}
}else{
System.out.println("没要找到要删除的节点编号---");
}
}
五、根据编号修改节点:
//根据编号修改信息
public void update(DoubleLinkedNode newNode){
DoubleLinkedNode curr = headNode;
boolean flag = false;
while(true){
if(curr == null){
break;
}
if(curr.getNo() == newNode.getNo()){
//直接设置找到的数据域
flag = true;
break;
}
curr = curr.getNext();
}
if(flag){
curr.setName(newNode.getName());
}else{
System.out.println("没有找到要修改的节点信息---");
}
}
六、根据编号查找节点:
//根据编号查找节点信息
public DoubleLinkedNode findByNo(Integer no){
DoubleLinkedNode curr = headNode;
boolean flag = false;
while(true){
if (curr == null){
break;
}
if(curr.getNo() == no){
flag = true;
break;
}
curr = curr.getNext();
}
if(flag){
return curr;
}else{
System.out.println("没有此编号的信息~~~~~");
return null;
}
}
测试:
public class DoubLinkedListTest3 {
public static void main(String[] args) {
DoubLinkedList3 doubLinkedList3 = new DoubLinkedList3();
doubLinkedList3.addTail(1,"张1");
doubLinkedList3.addTail(2,"张2");
doubLinkedList3.addTail(3,"张3");
doubLinkedList3.addTail(4,"张4");
System.out.println("正向遍历链表:");
doubLinkedList3.showFirst();
System.out.println("\n反向遍历:");
doubLinkedList3.showTail();
//尾插法如何从头遍历
System.out.println("\n删除节点后正向遍历==");
doubLinkedList3.del(1);
doubLinkedList3.showFirst();
System.out.println("\n根据编号修改节点");
doubLinkedList3.update(new DoubleLinkedNode(2,"xxx"));
doubLinkedList3.showFirst();
System.out.println("\n根据编号查找节点");
System.out.println(doubLinkedList3.findByNo(3));
}
}
七、双向链表按编号顺序插入节点:
package com.fan.linkedlist;
public class DoubleLinkedStudentDemo {
public static void main(String[] args) {
DoubleLinkedStudent doubleLinkedStudent = new DoubleLinkedStudent();
doubleLinkedStudent.addByOrder(new Student(2,"4"));
doubleLinkedStudent.addByOrder(new Student(3,"3"));
doubleLinkedStudent.addByOrder(new Student(1,"1"));
doubleLinkedStudent.addByOrder(new Student(4,"tong1"));
doubleLinkedStudent.show();
}
}
class DoubleLinkedStudent {
//设置头节点,数据域为空,没有意义,但是节点本身不为空,即headNode!=null
Student headNode = new Student(0,"");
public void addByOrder(Student newNode){
Student curr = headNode;//从头节点开始遍历
boolean flag = false;//判断编号是否有相同的
boolean isTail = false;//判断是否是到链表尾部了
while(true){
//注意,这几个if次序不能颠倒
if(newNode.getNo() < curr.getNo()){ //2<0,假
break;
}
if(curr.getNext() == null){
isTail = true; //到了链表尾部
break;
}
if(newNode.getNo() == curr.getNo()){
flag = true;//不能添加,编号相同
break;
}
curr = curr.getNext();
}
if(flag){
System.out.println(newNode.getNo()+"编号相同,不能添加");
}else if(isTail){//到了链表尾部,则添加新链表到末尾
curr.setNext(newNode);
newNode.setPre(curr);
}else{
//以下4行代码次序也不能顺序乱了
newNode.setNext(curr);
curr.getPre().setNext(newNode);
newNode.setPre(curr.getPre());
curr.setPre(newNode);
}
}
//遍历双线链表
public void show(){
Student curr= headNode.getNext();
while(true){
if(curr == null){
break;
}
System.out.println(curr);
curr = curr.getNext();
}
}
}
//节点类
class Student {
private Integer no;
private String name;
private Student pre;//指向上一个元素
private Student next;//指向下一个元素
public Student(Integer no, String name) {
this.no = no;
this.name = name;
}
public Student() {
}
public Integer getNo() {
return no;
}
public void setNo(Integer no) {
this.no = no;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student getPre() {
return pre;
}
public void setPre(Student pre) {
this.pre = pre;
}
public Student getNext() {
return next;
}
public void setNext(Student next) {
this.next = next;
}
@Override
public String toString() {
return "Student{" +
"no=" + no +
", name='" + name + '\'' +
'}';
}
}
思路:
先遍历原双向链表,如果找到链表中有元素的num比要插入元素的num大着停止然后将该要插入的元素放到链表中,
或者是找到的同样的num值那么就输出提示信息
或者链表为空、链表遍历完也没有在链表中找到比要插入的元素还大的元素则把要插入的元素放到链表的最后