博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据结构之链表
阅读量:5280 次
发布时间:2019-06-14

本文共 13481 字,大约阅读时间需要 44 分钟。

单链表学习

学习第一步:定义存放链表结点的类Node,类中包含两个字段:data字段和next字段,data字段是结点中的数值域,next是指向链表下一个结点的引用

      

1 //存放单链表节点 2 class Node { 3     private int data; 4     private Node next; 5  6     public Node(int data, Node next) { 7         this.data = data; 8         this.next = next; 9     }10 11     public int getData() {12         return data;13     }14 15     public void setData(int data) {16         this.data = data;17     }18 19     public Node getNext() {20         return next;21     }22 23     public void setNext(Node next) {24         this.next = next;25     }26 27     public void displayNode() {28         System.out.println("data:" + data);29     }30 31 }

学习第二步:学习单链表的一些操作,插入、删除、查找结点

1 // 单链表的一些操作 2 class LinkList { 3     private Node head; 4  5     public LinkList() { 6         head = null; 7     } 8  9     // 链表的头部插入元素10     public void insertNode(int data) {11         Node newNode = new Node(data, null);12         newNode.setNext(head);13         head = newNode;14     }15 16     // 从链表的头部删除元素17     public Node deleteNode() {18         Node tempNode = head;19         head = head.getNext();20         return tempNode;21     }22 23     // 打印链表中的结点的数值域的值24     public void displayList() {25         Node currentLinkList = head;26         while (currentLinkList != null) {27             currentLinkList.displayNode();28             currentLinkList = currentLinkList.getNext();29         }30     }31 32     // 判断单链表是否为空33     public boolean isEmpty() {34         return head == null;35     }36 37     // 单链表查询是否有关键字为key的结点38     public Node find(int key) {39         Node current = head;40         while (current.getData() != key) {41             if (current.getNext() == null) {42                 return null;43             }44             current = current.getNext();45         }46         return current;47 48     }49 50     // 单链表中删除指定关键字的结点51     public Node delete(int key) {52         // preNode要删除的结点的前一个结点53         Node preNode = head;54         // 标记要删除的结点55         Node current = head;56         while (current.getData() != key) {57             if (current == null) {58                 return null;59             } else {60                 preNode = current;61                 current = current.getNext();62             }63         }64         // 要删除的是头head指向的结点65         if (current == head) {66             head = head.getNext();67         }68         // 删除结点,preNode指向删除结点的后继结点69         else {70             preNode.setNext(current.getNext());71         }72         return current;73 74     }75 }

 

双端链表学习

可以看到我们上面的操作,在链表尾插入、删除结点的操作,虽然也可以做,但是需要从头遍历到尾部,这种方法的效率有点低,双端链表可以解决这个问题。双端链表循序我们跟操作链表头一样去操作链表尾,使得在一些特殊情况下,链表的操作更加便利。其实就是加了一个tail引用,指向链表的最后一个结点。

                       

我们在将结点顺序插入到链表中

1 public void insertAtTail(int data) {2         Node newNode = new Node(data, null);3         tail.setNext(newNode);4         tail = newNode;5         size++;6     }

但不幸的是双端链表也不能有助于删除最后一个链结点,因为没有一个引用指向倒数第二个链结点。如果最后一个链结点被删除,倒数第二个链结点的next字段应该变为null值,为了方便进行这一操作,我们此刻就需要双向链表,稍后介绍。

链表和数组的区别:

二者都属于一种数据结构 从逻辑结构来看 1. 数组必须事先定义固定的长度(元素个数),不能适应数据动态地增减的情况。当数据增加时,可能超出原先定义的元素个数;当数据减少时,造成内存浪费;数组可以根据下标直接存取。 2. 链表动态地进行存储分配,可以适应数据动态地增减的情况,且可以方便地插入、删除数据项。(数组中插入、删除数据项时,需要移动其它数据项,非常繁琐)链表必须根据next指针找到    下一个元素    从内存存储来看 1. (静态)数组从栈中分配空间, 对于程序员方便快速,但是自由度小 2. 链表从堆中分配空间, 自由度大但是申请管理比较麻烦 从上面的比较可以看出,如果需要快速访问数据,很少或不插入和删除元素,就应该用数组;相反,如果需要经常插入和删除元素就需要用链表数据结构了。 双端链表的一个实际应用:模拟队列

  定义存储结点的Node类

1 public class Node {2 3     public  int data;4     public Node next;5     public Node(int data,Node next){6         this.data=data;7         this.next=next;8     }9 }

定义链表操作

1 public class LinkList { 2  3     private Node head; 4     private Node tail; 5     public LinkList(){ 6         head=null; 7         tail=null; 8     } 9     public void insertLast(int data){10         Node newNode=new Node(data, null);11         if(isEmpty()){12             head=newNode;13         }14         else {15             tail.next=newNode;16         }17         tail=newNode;18     }19     public int deleteHead(){20         if(head.next==null){21             tail=null;22             return head.data;23         }24         if(head!=null){25             26             Node tempNode=head;27             head=head.next;28             return tempNode.data;29         }30         else {31             throw new RuntimeException("不能删除元素了");32         }33     }34     public boolean isEmpty(){35         return head==null;36     }37     38 }

链表模拟队列

1 //用的是insertLast()和deleteHead()方法,即在尾部插入,在链表头删除 2 public class LinkQueue { 3  4     private LinkList linkList; 5     public LinkQueue(){ 6         linkList=new LinkList(); 7     } 8     public void push(int data){ 9         linkList.insertLast(data);10     }11     public int pop(){12         return linkList.deleteHead();13     }14     public boolean isEmpty(){15         return linkList.isEmpty();16     }17 }

测试类

1 public class QueuqTest { 2  3     /** 4      * @param args 5      */ 6     public static void main(String[] args) { 7  8         LinkQueue linkQueue=new LinkQueue(); 9         linkQueue.push(10);10         linkQueue.push(20);11         linkQueue.push(30);12         linkQueue.push(40);13         System.out.println(linkQueue.pop());14     }15 }

有序链表学习

有序链表的结点插入和普通链表不同,其他操作相同,我们这里只给出往有序链表中插入结点的操作,有序列表适合用于需要频繁操作最值(最大或最小),而又不要求快速插入的场合

1 public void insert2SortLink(int data) { 2         Node newNode = new Node(data, null); 3         Node preNode = null; 4         Node currenNode = head; 5         //一直往后查找结点的插入位置直到链表结束或者data不大于currentNode.data 6         //while循环结束,结点的插入位置可能为表头、表尾、表中间的某个位置,或者链表是空表 7         while (currenNode != null && data > currenNode.data) { 8             preNode = currenNode; 9             currenNode = currenNode.next;10         }11         //插入点的前一个结点preNode指向结点=null,表明插入点在表头或者表是空表12         if (preNode == null) {13             head=newNode;14         }15         //插入点在表中间或者在表的尾部16         else {17             preNode.next=newNode;18         }19         //无论插入点在哪一个位置,新插入的结点的next域都指向currentNode20         newNode.next=currenNode;21     }

有序链表的一个应用:表插入排序

有序链表可以用于一种高效的排序机制。假设有一个无序的数组,如果从这个数组中取出数据,然后一个一个地插入到有序链表,他们自动的按顺序排序。把他们从有序链表中取出再放到数组中,那么数组就排好顺序了

表插入排序的效率:插入N个数据,就进行了n^2/2次比较,但移动仅进行了2*N次,效率,相对于简单排序中的插入排序的O(n2),还是要效率高一些的。

具体代码实现:

(1)Node类:

1 public class Node { 2  3     public  int data; 4     public Node next; 5     public Node(int data,Node next){ 6         this.data=data; 7         this.next=next; 8     } 9     //显示每个结点的信息10     public void displayNode(){11         System.out.print("data:"+data+" ");12     }13 }

(2)SortList类

1 class SortList { 2     private Node head; 3  4     public SortList() { 5         head = null; 6     } 7  8     public SortList(Node[] nodes) { 9         head = null;10         for (int j = 0; j < nodes.length; j++) {11             insert2SortLink(nodes[j]);12         }13     }14 15     public void insert2SortLink(Node newNode) {16         // Node thenewNode = newNode;17         Node preNode = null;18         Node currenNode = head;19         // 一直往后查找结点的插入位置直到链表结束或者data不大于currentNode.data20         // while循环结束,结点的插入位置可能为表头、表尾、表中间的某个位置,或者链表是空表21         while (currenNode != null && newNode.data > currenNode.data) {22             preNode = currenNode;23             currenNode = currenNode.next;24         }25         // 插入点的前一个结点preNode指向结点=null,表明插入点在表头或者表是空表26         if (preNode == null) {27             head = newNode;28         }29         // 插入点在表中间或者在表的尾部30         else {31             preNode.next = newNode;32         }33         // 无论插入点在哪一个位置,新插入的结点的next域都指向currentNode34         newNode.next = currenNode;35     }36 37     public Node deleteHead() {38         if (!isEmpty()) {39             Node tempNode = head;40             head = head.next;41             return tempNode;42         } else {43             throw new RuntimeException("不能删除元素了");44         }45     }46 47     public boolean isEmpty() {48         return head == null;49     }50 }

(3)测试类

1 public class SortedListTest { 2  3     /** 4      * @param args 5      */ 6     public static void main(String[] args) { 7  8         Node nodeArray[] = new Node[10]; 9         int n;10         for (int j = 0; j < nodeArray.length; j++) {11             n = (int) (Math.random() * 99);12             Node newNode = new Node(n, null);13             nodeArray[j] = newNode;14         }15         System.out.println("--------Unsorted Array--------");16         for (int j = 0; j < nodeArray.length; j++) {17             nodeArray[j].displayNode();18         }19         System.out.println();20         SortList sortList = new SortList(nodeArray);21         System.out.println("--------Sorted Array-------");22         for (int j = 0; j < nodeArray.length; j++) {23             nodeArray[j] = sortList.deleteHead();24             System.out.print(nodeArray[j].data + " ");25         }26         System.out.println("---------结束线----------");27 28     }29 30 }

学习双向链表

                          

 

双向链表提供了这样一种能力:允许向前遍历链表,也允许向后遍历链表,秘密在于每个结点存在两个指向其他链结点的引用。

双向链表的缺点:

每次插入或者删除一个结点的时候要处理四个引用,四个引用相对于两个引用,占用空间也变得大了一些,双向链表不必是双端链表,但这种方式(保留对链表最后一个元素的引用)有时候会非常的有用。

具体代码实现:

1 package LinkListQueue;  2   3 class DoubleLinkList {  4     class Node {  5         private int data;  6         private Node next = null;  7         private Node previous = null;  8   9         public Node(int data) { 10             this.data = data; 11         } 12     } 13  14     private Node head = null; 15     private Node tail = null; 16  17     // 在双向链表的头部插入 18     public void insertHead(int data) { 19         Node newNode = new Node(data); 20         // 判断链表是否为空,可以重新组织一下代码,把if-else都要执行的head=newNode这条语句移动到else代码块后面 21         if (head == null) { 22             head = newNode; 23             tail = newNode; 24         } else { 25             newNode.next = head; 26             head.previous = newNode; 27             head = newNode; 28         } 29         // LOOP:就是这个位置 30     } 31  32     // 在双向链表的尾部插入 33     public void insertTail(int data) { 34         Node newNode = new Node(data); 35         if (head == null) { 36             head = newNode; 37         } else { 38             tail.next = newNode; 39             newNode.previous = tail; 40         } 41         tail = newNode; 42     } 43  44     // 在链表的内部的某个位置的后面插入元素,我們假定链表非空 45     public boolean insertAfter(int target, int data) { 46         Node newNode = new Node(data); 47         Node current = head; 48         while (current.data != target) { 49             current=current.next; 50     } 51             if (current == tail) { 52                 newNode.next=null; 53                 tail=newNode; 54             } else { 55                 newNode.next=current.next; 56                 current.next.previous = newNode; 57             } 58             current.next = newNode; 59             newNode.previous=current; 60         return true; 61 } 62  63     // 在链表的头部进行删除 64     public Node deleteHead() { 65         if (head == null) { 66             throw new RuntimeException("不能删除节点了"); 67         } 68         Node tmpNode = head; 69         if (head.next == null) { 70             head = null; 71             tail = null; 72         } else { 73             head.next.previous = null; 74             head = head.next; 75         } 76         return tmpNode; 77     } 78  79     // 在链表的尾部进行删除 80     public void  deleteTail() { 81         if (head == null) { 82             throw new RuntimeException("不能删除节点了"); 83         } 84         Node tmpNode = tail; 85         //如果只有一个元素 86         if (head.next == null) { 87             head = null; 88             tail = null; 89         } else { 90             tail.previous.next = null; 91             tail = tail.previous; 92         } 93     } 94  95     // 在链表的内部的某个位置删除指定关键字的结点元素 96     public Node deleteKey(int target) { 97         // 链表为空表,不能删除 98         if (head == null) { 99             throw new RuntimeException("不能删除元素");100         }101         Node current = head;102         while (current.data != target) {103             current = current.next;104             if (current == null) {105                 return null;106             }107         }108         // 最关键的理解点,有点绕,引用变化:删除结点的前一个结点的next->删除结点的后一个结点109         // 删除结点的后一个结点的previous->删除结点的前一个结点110         if (current == head) {111             head = current.next;112         } else {113             current.previous.next = current.next;114         }115         if (current == tail) {116             tail = current.previous;117         } else {118             current.next.previous = current.previous;119         }120         return current;121     }122 123     public void displayList() {124         System.out.println("Head-->Tail");125         Node current = head;126         while (current != null) {127             System.out.print(current.data + " ");128             current=current.next;129         }130         System.out.println();131     }132 }133 134 public class DouLinkListTest {135 136     /**137      * @param args138      */139     public static void main(String[] args) {140 141         DoubleLinkList dll = new DoubleLinkList();142         dll.insertHead(1);143         dll.insertTail(3);144         dll.insertAfter(1, 2);145         dll.insertAfter(3, 4);146         dll.insertAfter(4, 5);147         dll.displayList();148         dll.deleteHead();149         dll.displayList();150         dll.deleteTail();151         dll.displayList();152         dll.deleteKey(3);153         dll.displayList();154         dll.deleteKey(2);155         dll.displayList();156     }157 158 }
View Code

 

转载于:https://www.cnblogs.com/ysw-go/p/5399138.html

你可能感兴趣的文章
梦断代码读后感01
查看>>
[CF#250 Div.2 D]The Child and Zoo(并查集)
查看>>
博客园博客插入公式
查看>>
hdu 1028 Ignatius and the Princess III(母函数入门+模板)
查看>>
Ubuntu下配置安装telnet server
查看>>
Codeforces 235 E Number Challenge
查看>>
ubuntu 常见命令整理
查看>>
EJBCA安装教程+postgresql+wildfly10
查看>>
(五十四)涂鸦的实现和截图的保存
查看>>
配置EditPlus使其可以编译运行java程序
查看>>
java中的占位符\t\n\r\f
查看>>
7.14
查看>>
SDN2017 第一次作业
查看>>
MySQL通过frm 和 ibd 恢复数据过程
查看>>
SRS源码——Listener
查看>>
web.xml 4.0 头
查看>>
Java面向对象抽象类案例分析
查看>>
100.Same Tree
查看>>
对SPI、IIC、IIS、UART、CAN、SDIO、GPIO的解释
查看>>
Thymeleaf模板格式化LocalDatetime时间格式
查看>>