数据结构和算法03 之链表
2023-09-14 09:01:03 时间
在第一章的数组中,我们看到数组作为数据存储结构有一定的缺陷。在无序数组中,搜索时低效的;而在有序数组中,插入效率又很低;不管在哪一种数组中删除效率都很低。况且一个数组创建后,它的大小是无法改变的。
在本章中,我们将讨论下链表这个数据结构,它可以解决上面的一些问题。链表可能是继数组之后第二种使用得最广泛的通用数据结构了。本章主要讨论单链表和双向链表。
顾名思义,单链表只能从表头到表尾的顺序,每个节点中保存了指向下一个节点的指针;双向链表则可以反向遍历,因为节点中既保存了向后节点的指针,又保存了向前节点的指针。由于链表结构相对简单,这里不再赘述,直接通过程序来查看它们常见的操作:
单链表:public void insertFirst(int value) {//添加头结点 Link newLink = new Link(value); newLink.next = first; //newLink-- old first first = newLink; //first-- newLink nElem ++; } public Link deleteFirst() { //删除头结点 if(isEmpty()) { System.out.println("链表为空!"); return null; } Link temp = first; first = first.next; nElem --; return temp; } /************************************************************ ***下面是有序链表的插入*** ***这样简单排序就可以用链表来实现,复杂度为O(N) ***定义一个方法,传入一个数组, ***在方法内,遍历数组并调用insert方法就可以将数组中的数据排好序 ***********************************************************/ public void insert(int value) { Link newLink = new Link(value); Link previous = null; Link current = first; while(current != null value current.data) { previous = current; current = current.next; } if(previous == null) { first = newLink; } else { previous.next = newLink; } newLink.next = current; nElem ++; } public Link find(int value) { //查找特定的节点 Link current = first; while(current.data != value) { if(current.next == null) return null; else current = current.next; } return current; } public Link delete(int value) { //删除特定的节点 Link current = first; Link previous = first; while(current.data != value) { if(current.next == null) { return null; } previous = current; current = current.next; } if(current == first) { first = first.next; } else { previous.next = current.next; } nElem --; return current; } public void displayList() { if(isEmpty()){ System.out.println("链表为空!"); return; } Link current = first; while(current != null) { current.displayLink(); current = current.next; } System.out.println(" "); } public boolean isEmpty() { return (first == null); } public int size() { return nElem; } class Link { public int data; public Link next; public Link(int data) { this.data = data; this.next = null; } public void displayLink() { System.out.print("{" + data + "} "); }
public void insertFirst(long value) { //插入头节点 Node newLink = new Node(value); if(isEmpty()) { last = newLink; } else{ first.previous = newLink; //newLink -- oldFirst.previous newLink.next = first; //newLink.next -- first } first = newLink; //first -- newLink size ++; } public void insertLast(long value) {//插入尾节点 Node newLink = new Node(value); if(isEmpty()){ first = newLink; } else { last.next = newLink; //newLink -- oldLast.next newLink.previous = last; //newLink.previous -- last } last = newLink; size ++; } public Node deleteFirst() {//删除头结点 if(first == null) { System.out.println("链表为空!"); return null; } Node temp = first; if(first.next == null) { //只有一个节点 last = null; } else { first.next.previous = null; } first = first.next; size --; return temp; } public Node deleteLast() {//删除尾节点 if(last == null) { System.out.println("链表为空"); return null; } Node temp = last; if(last.previous == null) { //只有一个节点 first = null; } else { last.previous.next = null; } last = last.previous; size --; return temp; } public boolean insertAfter(long key, long value) { //在key后面插入值为value的新节点 Node current = first; while(current.data != key) { current = current.next; if(current == null) { System.out.println("不存在值为" + value + "的节点!"); return false; } } if(current == last) { insertLast(value); } else { Node newLink = new Node(value); newLink.next = current.next; current.next.previous = newLink; newLink.previous = current; current.next = newLink; size ++; } return true; } public Node deleteNode(long key) {//删除指定位置的节点 Node current = first; while(current.data != key) { current = current.next; if(current == null) { System.out.println("不存在该节点!"); return null; } } if(current == first) { deleteFirst(); } else if(current == last){ deleteLast(); } else { current.previous.next = current.next; current.next.previous = current.previous; size --; } return current; } public void displayForward() { //从头到尾遍历链表 System.out.println("List(first -- last): "); Node current = first; while(current != null) { current.displayLink(); current = current.next; } System.out.println(" "); } public void displayBackward() { //从尾到头遍历链表 System.out.println("List(last -- first): "); Node current = last; while(current != null) { current.displayLink(); current = current.previous; } System.out.println(" "); } class Node { public long data; public Node next; public Node previous; public Node(long value) { data = value; next = null; previous = null; } public void displayLink() { System.out.print(data + " "); }
在表头插入和删除速度很快,仅需改变一两个引用值,所以话费O(1)的时间。平均起来,查找、删除和在指定节点后面插入都需要搜索链表中的一半节点,需要O(N)次比较。在数组中执行这些操作也需要O(N)次比较,但是链表仍然要比数组快一些,因为当插入和删除节点时,链表不需要移动任何东西,增加的效率是很显著的,特别是当复制时间远远大于比较时间的时候。
当然,链表比数组优越的另一个重要方面是链表需要多少内存就可以用多少内存,并且可以扩展到所有可用内存。数组的大小在它创建的时候就固定了,所以经常由于数组太大导致效率低下,或者数组太小导致空间溢出。可变数组可以解决这个问题,但是它经常只允许以固定大小的增量扩展,这个解决方案在内存使用率上来说还是比链表要低。
链表比较简单,就讨论这么多,如有问题,欢迎留言指正~
转载:http://blog.csdn.net/eson_15/article/details/51136653
【数组与链表算法】矩阵算法在程序中常见的简单应用 | C++ 数组与链表都是相当重要的结构化数据类型,也都是典型线性表的应用。线性表用于计算机中的数据存储结构,按照内存存储的方式基本上可以分为以下两种:静态数据结构和动态数据结构。数组类型就是一种典型的静态数据结构,动态数据结构又称为链表。在我前面的算法系列文章都细致的对二者的使用方法做过讲解。
相关文章
- 日拱算法:典例-快慢指针解“环形链表”
- LeetCode.160. 相交链表
- 【大厂高频算法题】判断链表中是否有环
- 143. 重排链表
- 114. 二叉树展开为链表
- leetcode-234. 回文链表
- 删除链表的倒数第n个节点
- 《算法竞赛进阶指南》0x13 链表与邻接表
- 常用链表排序算法_单链表的排序算法
- Go 数据结构和算法篇(一):链表
- 循环队列出队-单个指针下循环链表的入队与出队
- BAT算法面试题--环形链表(哈希表法)
- 寻找两个链表相交节点方法(可以是有环链表)
- 算法-链表实现栈详解手机开发
- 算法-删除链表中重复的结点详解编程语言
- 算法-从尾到头打印链表详解编程语言
- 算法-复杂链表的复制详解编程语言
- 算法练习之环形链表详解编程语言
- 链接Linux: 开启实现更多技术目标的突破口(链表linux)
- 从Redis链表中受益优化缓存策略(redis链表缓存)
- 深入理解链表的各类操作详解
- STLlist链表的用法详细解析
- C语言实现输出链表中倒数第k个节点