* 注: 本文/本系列谢绝转载,如有转载,本人有权利追究相应责任。 2019年4月8日

Stan Zhang 2019年4月8日  格物致知,经世致用。

[面试题]1.为什么要用链表?

数组具有的缺陷: 数组是长度固定类型固定的,并且它取值快,插入和删除慢

链表正弥补了这样的不足,它是长度都可以灵活扩展的,另外它是插入和删除快,但是取值非常慢

查找也比较慢,这是这种线性结构的通病,需要通过散列的思路进行解决,当然一个例外是

有下标的有序线性结构可以使用二分查找提高查找效率

因此没有最好的,只有最合适的数据结构。

2.链表的分类.

A.单链表:

B:双端链表

C:有序链表 : 没有什么突出的优点,只是为了迎合扩展性(长度扩展)以及业务

故名思意,有序链表的序列是有序的。

先比较一下有序数组,有序数组牺牲插入效率来提升查找效率,配合二分查找(利用数组索引)可以LogN级别定位元素。

但是有序链表中有序并不能提升其查找效率(它没有数组的索引)。

List本生就是按照添加的顺序有序的,再引入一个排序标准没有必要,因此Java当中没有SortedList.

而Set本身没有顺序规则,可以设置一个排序,因此当需要使用有序的集合时,我们可以使用SortedSet.

D:双向链表

3.实现

A.单链表

单链表的特点是头部插入快,其他无论是查找、尾部插入还是删除,效率都是N级别的。

Java代码的实现:

package ds1.linked.table.singleDirection;

/**
* 单向链表
* 单向链表的表中会存留一个链表的起始端 Node first,这导致从尾部插入数据非常麻烦需要花费O(N)的时间
*/
public class SingleDirectionList {
static class Node {
Node next;
String data;
Node(String data){
this.data = data;
}
} static class LinkedList{
Node first; // 保存第一个节点 public LinkedList(){
} public LinkedList(Node first) {
this.first = first;
} public void foreachPrint(){
if(first == null){
System.out.println("[]");
}else{
System.out.print("[" + this.first.data);
Node current = first.next;
while(current != null){
System.out.print("," + current.data);
current = current.next;
}
System.out.println("]");
}
} /**
* 插入节点的时间复杂度: O(1)
* @param node
*/
public void addNode(Node node){
if(first == null){
this.first = node;
}else{
node.next = this.first;
this.first = node;
}
} /**
* 删除节点的时间复杂度: O(N)
* @param node
*/
public void removeNode(Node node){
if(this.first == null){
return;
} if(this.first.equals(node)){
this.first = this.first.next;
node.next = null;
return;
}
Node current = this.first;
Node before = current;
while(current.next != null){
if(current.equals(node)){
before.next = current.next;
node.next = null;
break;
}
before = current;
current = current.next;
}
} /**
* 查找节点的时间复杂度: O(N)
* @param data
* @return
*/
public Node findNodeByData(String data){
Node result = null;
Node current = this.first;
while(current != null){
if(current.data != null && current.data.equals(data)){
result = current;
break;
}
current = current.next;
}
return current;
} } public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
Node node4 = new Node("node4");
Node node5 = new Node("node5");
Node node6 = new Node("node6");
Node node7 = new Node("node7");
linkedList.addNode(node1);
linkedList.addNode(node2);
linkedList.addNode(node3);
linkedList.addNode(node4);
linkedList.addNode(node5);
linkedList.addNode(node6);
linkedList.addNode(node7);
linkedList.foreachPrint();
Node tmpNode4 = linkedList.findNodeByData("node4");
linkedList.foreachPrint();
linkedList.removeNode(tmpNode4);
linkedList.foreachPrint();
}
}

结果:

[node7,node6,node5,node4,node3,node2,node1]
[node7,node6,node5,node4,node3,node2,node1]
[node7,node6,node5,node3,node2,node1]

B.双端链表

双端链表具有两个端点指针,分别指向收尾指针。它具有首位快速插入,首部快速删除的能力。但是因为还是单向的,因此尾部删除性能不佳。

Java代码:

package ds1.linked.table.twoHead;

import ds1.linked.table.singleDirection.SingleDirectionList;

/**
* 双端列表
* 有两个端点
*/
public class TwoHeadLinkedList {
static class Node{
Node next;
String data; public Node(String data) {
this.data = data;
}
} static class LinkedList{
Node first;
Node tail; public LinkedList(){
} public LinkedList(Node node){
this();
addNodeToHead(node);
} /**
* 从首部添加一个节点
* 常数时间
* @param node
*/
public void addNodeToHead(Node node){
if(this.first == null){
this.first = node;
this.tail = node;
}else{
node.next = this.first;
this.first = node;
}
} /**
* 删除头节点
* 常数时间
*/
public void removeHead(){
if(this.first != null){
if(this.first == this.tail){
this.tail = null;
}
this.first = this.first.next;
}
} /**
* 添加尾部节点
* 常数时间
* @param node
*/
public void addNodeTail(Node node){
if(this.tail != null){
this.tail.next = node;
this.tail = node;
}else{
this.first = this.tail = node;
}
} /**
* 删除尾部节点
* 线性时间
*/
public void removeTailNode(){
if(this.first == null){
return;
}
if(this.first == this.tail){
this.first = this.tail = null;
return;
} Node current = this.first;
Node before = current;
while(current.next != null){
before = current;
current = current.next;
}
before.next = null;
this.tail = before;
} /**
* 查找一个节点
* 时间复杂度: 线性时间
* @param data
* @return
*/
public Node findByData(String data){
Node result = null;
if(this.first == null){
return result;
}
Node current = this.first;
while(current != null){
if(current.data != null && current.data.equals(data)){
result = current;
break;
}
current = current.next;
} return result;
} /**
* 删除一个节点
* 时间复杂度:线性时间
* @param node
*/
public void removeNode(Node node){
if(this.first == null){
return;
} if(this.first.equals(node)){
this.first = node.next;
} Node current = this.first;
Node before = current;
while(current != null){
if(current == node){
before.next = current.next;
break;
}
before = current;
current = current.next;
} if(this.tail == node){
this.tail = before;
}
} public void foreachPrint(){
if(first == null){
System.out.println("[]");
}else{
System.out.print("[" + this.first.data);
Node current = first.next;
while(current != null){
System.out.print("," + current.data);
current = current.next;
}
System.out.println("]");
}
} } public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
Node node4 = new Node("node4");
Node node5 = new Node("node5");
Node node6 = new Node("node6");
Node node7 = new Node("node7");
linkedList.addNodeToHead(node1);
linkedList.foreachPrint();
linkedList.addNodeToHead(node2);
linkedList.foreachPrint();
linkedList.addNodeTail(node3);
linkedList.foreachPrint();
linkedList.addNodeToHead(node4);
linkedList.foreachPrint();
linkedList.addNodeTail(node5);
linkedList.foreachPrint();
linkedList.addNodeToHead(node6);
linkedList.foreachPrint();
linkedList.addNodeTail(node7);
linkedList.foreachPrint();
Node tmpNode4 = linkedList.findByData("node4");
linkedList.foreachPrint();
linkedList.removeNode(tmpNode4);
linkedList.foreachPrint();
} }

结果:

[node1]
[node2,node1]
[node2,node1,node3]
[node4,node2,node1,node3]
[node4,node2,node1,node3,node5]
[node6,node4,node2,node1,node3,node5]
[node6,node4,node2,node1,node3,node5,node7]
[node6,node4,node2,node1,node3,node5,node7]
[node6,node2,node1,node3,node5,node7]

C:有序链表 : 没有什么突出的优点,只是为了迎合扩展性(长度扩展/类型扩展)以及业务

package ds1.linked.table.orderedList;

public class OrderedList {
static class Node{
Node next;
long data; public Node(long data) {
this.data = data;
}
} static class LinkedList{
Node first; LinkedList(){
} public void foreachPrint(){
if(first == null){
System.out.println("[]");
}else{
System.out.print("[" + this.first.data);
Node current = first.next;
while(current != null){
System.out.print("," + current.data);
current = current.next;
}
System.out.println("]");
}
} /**
* 插入过程,线性时间
* @param node
*/
public void insert(Node node){
if(this.first == null){
this.first = node;
return;
} if(this.first.data > node.data){
node.next = this.first;
this.first = node;
return;
} Node current = this.first;
Node before = current;
while(current != null){
if(current.data > node.data){
node.next = current;
before.next = node;
break;
}
before = current;
current = current.next;
} if(current == null){
before.next = node;
} } /**
* 查找过程: 不能利用二分查找,还是线性时间
* @param data
*/
public Node findByValue(long data){
Node result = null;
Node current = this.first;
while (current != null){
if(current.data == data){
result = current;
break;
}
current = current.next;
}
return result;
}
} public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
linkedList.insert(new Node(100));
linkedList.foreachPrint();
linkedList.insert(new Node(200));
linkedList.foreachPrint();
linkedList.insert(new Node(122));
linkedList.foreachPrint();
linkedList.insert(new Node(39));
linkedList.foreachPrint();
linkedList.insert(new Node(103));
linkedList.foreachPrint();
linkedList.insert(new Node(145));
linkedList.foreachPrint();
} }

结果:

[]
[,]
[,,]
[,,,]
[,,,,]
[,,,,,]

D:双向链表

双向链表很好的利用了双端和双向的优势,使得双端的插入删除操作变为常数级别。

package ds1.linked.table.twoHeadTwoDirection;

/**
* 双向链表
* 有双端点双向,也就意味着两头的插入删除效率都是线性的。
*/
public class TwoHeadTwoDirectionList {
static class Node {
Node next;
Node before;
String data;
Node(String data){
this.data = data;
}
} static class LinkedList{
Node first; // 保存第一个节点
Node tail; // 保留最后一个节点 public LinkedList(){
} public LinkedList(Node first) {
this.first = this.tail = first;
} public void foreachPrint(){
if(first == null){
System.out.println("[]");
}else{
System.out.print("[" + this.first.data);
Node current = first.next;
while(current != null){
System.out.print("," + current.data);
current = current.next;
}
System.out.println("]");
}
} /**
* 插入头节点的时间复杂度:线性
* @param node
*/
public void addNodeToHead(Node node){
if(first == null){
this.first = this.tail = node;
}else{
node.next = this.first;
this.first = node;
}
} /**
* 删除头节点的时间复杂度:线性时间
*/
public void removeNodeFromHead(){
if(this.first != null){
Node tmp = this.first.next;
this.first.next = null;
this.first = tmp;
}
} /**
* 尾部新增一个节点
* 线性时间
* @param node
*/
public void addNodeToTail(Node node){
if(this.tail == null){
this.tail = node;
}else{
node.before = this.tail;
this.tail.next = node;
this.tail = node;
}
} /**
* 从尾部删除节点
* 线性时间
*/
public void removeNodeFromTail(){
if(this.tail != null){
Node tmp = this.tail;
this.tail = this.tail.before;
this.tail.next = null;
tmp.before = null;
}
} /**
* 删除节点的时间复杂度: O(N)
* @param node
*/
public void removeNode(Node node){
if(this.first == null){
return;
} if(this.first.equals(node)){
this.first = this.first.next;
node.next = null;
return;
}
Node current = this.first;
Node before = current;
while(current.next != null){
if(current.equals(node)){
before.next = current.next;
before.next.before = before;
node.next = null;
node.before = null;
break;
}
before = current;
current = current.next;
}
} /**
* 查找节点的时间复杂度: O(N)
* @param data
* @return
*/
public Node findNodeByData(String data){
Node result = null;
Node current = this.first;
while(current != null){
if(current.data != null && current.data.equals(data)){
result = current;
break;
}
current = current.next;
}
return current;
} } public static void main(String[] args) {
LinkedList linkedList = new LinkedList();
Node node1 = new Node("node1");
Node node2 = new Node("node2");
Node node3 = new Node("node3");
Node node4 = new Node("node4");
Node node5 = new Node("node5");
Node node6 = new Node("node6");
Node node7 = new Node("node7");
linkedList.addNodeToHead(node1);
linkedList.foreachPrint();
linkedList.addNodeToHead(node2);
linkedList.foreachPrint();
linkedList.addNodeToTail(node3);
linkedList.foreachPrint();
linkedList.addNodeToHead(node4);
linkedList.foreachPrint();
linkedList.addNodeToTail(node5);
linkedList.foreachPrint();
linkedList.addNodeToTail(node6);
linkedList.foreachPrint();
linkedList.addNodeToHead(node7);
linkedList.foreachPrint();
Node tmpNode4 = linkedList.findNodeByData("node4");
linkedList.foreachPrint();
linkedList.removeNode(tmpNode4);
linkedList.foreachPrint();
linkedList.removeNodeFromHead();
linkedList.foreachPrint();
linkedList.removeNodeFromTail();
linkedList.foreachPrint();
linkedList.removeNodeFromTail();
linkedList.foreachPrint();
linkedList.removeNodeFromHead();
linkedList.foreachPrint();
}
}

结果:

[node1]
[node2,node1]
[node2,node1,node3]
[node4,node2,node1,node3]
[node4,node2,node1,node3,node5]
[node4,node2,node1,node3,node5,node6]
[node7,node4,node2,node1,node3,node5,node6]
[node7,node4,node2,node1,node3,node5,node6]
[node7,node2,node1,node3,node5,node6]
[node2,node1,node3,node5,node6]
[node2,node1,node3,node5]
[node2,node1,node3]
[node1,node3]

[数据结构]P1.1 链表结构的更多相关文章

  1. 数据结构:单链表结构字符串(python版)改进

    此篇文章的replace实现了字符串类的多次匹配,但依然有些不足. 因为python字符串对象为不变对象,所以replace方法并不修改原先的字符串,而是返回修改后的字符串. 而此字符串对象时用单链表 ...

  2. 数据结构:单链表结构字符串(python版)添加了三个新功能

    #!/urs/bin/env python # -*- coding:utf-8 -*- #异常类 class stringTypeError(TypeError): pass #节点类 class ...

  3. 数据结构:单链表结构字符串(python版)

    #!/urs/bin/env python # -*- coding:utf-8 -*- #异常类 class stringTypeError(TypeError): pass #节点类 class ...

  4. 【C&数据结构】---关于链表结构的前序插入和后序插入

    刷LeetCode题目,需要用到链表的知识,忽然发现自己对于链表的插入已经忘得差不多了,以前总觉得理解了记住了,但是发现真的好记性不如烂笔头,每一次得学习没有总结输出,基本等于没有学习.连复盘得机会都 ...

  5. 数据结构( Pyhon 语言描述 ) — — 第4章:数据和链表结构

    数据结构是表示一个集合中包含的数据的一个对象 数组数据结构 数组是一个数据结构 支持按照位置对某一项的随机访问,且这种访问的时间是常数 在创建数组时,给定了用于存储数据的位置的一个数目,并且数组的长度 ...

  6. 《Java数据结构》链表结构(单向链表,双向链表)

    单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始:链表是使用指针进行构造的列表:又称为结点列表,因为链表是由一个个结点组装起来的:其中每个结点都有指 ...

  7. python数据结构与算法——链表

    具体的数据结构可以参考下面的这两篇博客: python 数据结构之单链表的实现: http://www.cnblogs.com/yupeng/p/3413763.html python 数据结构之双向 ...

  8. [pjsip]Pjlib中的链表结构

    Pjlib的链表结构跟常见的链表结构有所区别,如下图所示: 图1:一般链表结构 图2:pjlib中的链表结构 可以看到一般的双向链表是链表节点包含数据域,而pjlib中是数据域包含链表节点.一般的链表 ...

  9. 数据结构与算法 —— 链表linked list(01)

    链表(维基百科) 链表(Linked list)是一种常见的基础数据结构,是一种线性表,但是并不会按线性的顺序存储数据,而是在每一个节点里存到下一个节点的指针(Pointer).由于不必须按顺序存储, ...

随机推荐

  1. Codeforces 1072 - A/B/C/D - (Done)

    链接:http://codeforces.com/contest/1072/ A - Golden Plate - [计算题] #include<bits/stdc++.h> using ...

  2. arcmap发布服务报错:“Faild to publish service”

    发布gp服务时,Analyze没有重大错误,但是发布结束时提示"Faild to publish service".让人很懵逼: 解决方法: 打开arcgis server man ...

  3. [qemu] 差分盘使用

    我要装好多台同样配置的虚拟机.比如10台CentOS7, 各自有不同的配置. 这样的话装10台kvm虚拟机,特别的浪费空间,image文件相互迁移的话也不方便. 这个时候可以选择差分盘:就是先装一个标 ...

  4. xss脚本绕过限制的方法

    第一关:第一关比较简单,直接写入标签就可以,这里不多说了,payload如下: http://sqler.win/xss/level1.php?name=test%3Csvg/onload=alert ...

  5. python终端打印带颜色的print

    原理 实现过程:       终端的字符颜色是用转义序列控制的,是文本模式下的系统显示功能,和具体的语言无关.       转义序列是以ESC开头,即用\033来完成(ESC的ASCII码用十进制表示 ...

  6. nginx安装lua模块实现高并发

    nginx安装lua扩展模块 1.下载安装LuaJIT-2.0.4.tar.gz wget -c http://luajit.org/download/LuaJIT-2.0.4.tar.gz tar ...

  7. VCS

    timing check相关的, +notimingcheck命令,可以用在compile时,也可以用在run time的时候, 都是将检查timing的系统函数,都disable掉了, 加在comp ...

  8. uva12545

    #include<iostream> using namespace std; +; char s[maxn],t[maxn]; int main(){ //freopen("1 ...

  9. Java并发——Fork/Join框架与ForkJoinPool

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4631466. ...

  10. ARM-ili9325屏调试1--时序

    2011-06-21 22:04:54 LCD连接好了,读id,不成功.说明配置引脚或读写时序不对. 原来是软件引脚配置出错. 应该用如下. #define LCD_CS   {3<<30 ...