什么是链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。(摘自百度百科)
单链表:每个结点内有数据域和指向下一个结点的next指针
双链表:每个结点内有数据域和指向下一个结点的next指针,还有指向上一个结点的pre指针
链表的基本操作
链表的基本操作包括增、删、改、查。查找操作应用在增、删、改操作之中。
- addNode():向链表添加结点
- delete():删除链表中的某个结点
- addNode():修改链表种某个结点的数据域
- display():链表的遍历
## 单链表 单链表的每个结点中都有一个用来存放数据的数据域和一个用于指向下一个结点的指针域next,用遍历的方式对单独的某个结点进行操作。
单链表因为只有next指针,所以只能向后遍历
创建单链表的结点类Node
创建类Node用于存放结点。
- 变量data用于存放数据。
- 变量next用于指向下一个结点。
- 创建构造方法初始化data的值。
- 重写toString方法用于输出结点内的data。
//单链表结点类
class Node {
public Node next; //指向下一个结点
public int data; //数据域
public Node(int data) { //初始化data
this.data = data;
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
创建单链表类SLinkedList
创建SLinkedList用于初始化头结点和存放各种操作方法。
- 创建一个新结点head代表头结点,头结点内没有值。
- 增、删、改等方法均放在此类实现。
class SLinkedList {
private Node head = new Node(-1);//头结点不存放任何数据
}
单链表的添加
使用尾插法添加结点,即从链表的尾部添加。
- 遍历链表找到最后一个结点。
- 从表尾插入新结点。
//从尾部添加结点
public void addNode(Node node) {
//创建指向头结点的指针,用于遍历链表
Node temp = head;
//使用temp指针遍历链表,找到最后一个结点
while(temp.next != null) {
temp = temp.next;
}
//添加结点
temp.next = node;
}
单链表的修改
根据数据域的值遍历链表找到待修改的结点,如果没有相符的数据域的结点则显示“没有找到”。
- 遍历链表找到待修改结点。
- 如找到则修改数据,找不到则显示“没有找到”。
//修改特定结点的数据,n为要修改的结点的数据值
public void update(int n,Node node) {
//指针指向头结点的下一个结点
Node temp = head.next;
boolean flag = true;
//找到数据域值为n的结点
while(temp.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//将新结点的数据域替换被修改结点
if(flag == true)
temp.data = node.data;
}
单链表的删除
根据数据域的值遍历链表找到待删除结点的上一个结点,如果没有相符的数据域的结点则显示“没有找到”。
- 遍历链表找到待删除结点的上一结点。
- 如找到则删除下一结点,找不到则显示“没有找到”。
//删除数据域值为n的结点
public void delete(int n) {
Node temp = head;
boolean flag = true;
//指针指向待删除结点的上一个结点
while(temp.next.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//删除结点
if(flag == true)
temp.next = temp.next.next;
}
单链表的遍历
使用指向head结点的temp指针对链表进行遍历。
//遍历链表
public void display() {
Node temp = head;
while(temp.next != null) {
temp = temp.next;
System.out.println(temp);
}
}
单链表完整代码
//单链表结点类
class Node {
public Node next; //指向下一个结点
public int data; //数据域
public Node(int data) {
this.data = data;
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
//单链表类
class SLinkedList {
private Node head = new Node(-1);//头结点不存放任何数据
//从尾部添加结点
public void addNode(Node node) {
//创建指向头结点的指针,用于遍历链表
Node temp = head;
//使用temp指针遍历链表,找到最后一个结点
while(temp.next != null) {
temp = temp.next;
}
//添加结点
temp.next = node;
}
//遍历链表
public void display() {
Node temp = head;
while(temp.next != null) {
temp = temp.next;
System.out.println(temp);
}
}
//修改特定结点的数据,n为要修改的结点的数据值
public void update(int n,Node node) {
//指针指向头结点的下一个结点
Node temp = head.next;
boolean flag = true;
//找到数据域值为n的结点
while(temp.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//将新结点的数据域替换被修改结点
if(flag == true)
temp.data = node.data;
}
//删除数据域值为n的结点
public void delete(int n) {
Node temp = head;
boolean flag = true;
//指针指向待删除结点的上一个结点
while(temp.next.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//删除结点
if(flag == true)
temp.next = temp.next.next;
}
}
## 双链表 双链表的每个结点中不仅都有一个用来存放数据的数据域和一个用于指向下一个结点的指针域next,还有一个指向上一结点的pre指针,也是用遍历的方式对单独的某个结点进行操作。
单链表因为只有next指针,所以只能向后遍历,双链表因为有pre指针,所以可以向前操作。单链表删除时需要找到待删除结点的上一结点,而双链表只需要找到待删除结点,然后对next和pre指针进行操作即可删除结点。
双链表完整代码
//双链表结点类
class DNode {
public DNode next; //指向下一个结点
public DNode pre; //指向上一个结点
public int data; //数据域
public DNode(int data) {
this.data = data;
}
@Override
public String toString() {
return "Node [data=" + data + "]";
}
}
//双链表类
class DLinkedList {
private DNode head = new DNode(-1); //头结点不存储任何数据
//从尾部添加结点
public void addNode(DNode node) {
//设置指向头结点的指针
DNode temp = head;
while(temp.next != null) {
temp = temp.next;
}
temp.next = node;
//修改pre指针
node.pre = temp;
}
//遍历链表
public void display() {
DNode temp = head;
while(temp.next != null) {
temp = temp.next;
System.out.println(temp);
}
}
//修改特定结点的数据,n为要修改的结点的数据值
public void update(int n,DNode node) {
//指针指向头结点的下一个结点
DNode temp = head.next;
boolean flag = true;
//找到数据域值为n的结点
while(temp.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//将新结点的数据域替换被修改结点
if(flag == true)
temp.data = node.data;
}
//删除数据域值为n的结点
public void delete(int n) {
DNode temp = head.next;
boolean flag = true;
//指针指向待删除结点的上一个结点
while(temp.data != n) {
if(temp.next == null) {
System.out.println("没有找到");
flag = false;
break;
}
temp = temp.next;
}
//删除结点
if(flag == true) {
//判断要删除的结点是否为最后一个结点
if(temp.next == null)
//待删除结点的前一个结点的next指针置为null
temp.pre.next = null;
else {
temp.pre.next = temp.next;
temp.next.pre = temp.pre;
}
}
}
}