目录
一:链表
链表是一种在物理内存上不连续的存储结构。线性表有顺序表和链表,顺序表的优点是查找数据较快,而插入和删除数据较慢,相反链表的优点是插入和删除数据较快(因为不需要进行移动数据),查找数据较慢(因为单链表只能从前继结点找后继结点,不能从后继结点找前继结点)。
链表的种类: 单链表,双链表,循环链表。
链表的组成:由一个个结点连接而成。
结点:由数据域和指针域组成
二:单链表
单链表种类:带头结点的单链表,不带头结点的单链表。
带头结点的单链表
不带头结点的单链表
带头结点的单链表中,头结点不存放数据,永远在链表的第一位,即使是一个空链表也需要有一个头结点,其目的是为了简化链表的插入和删除作用。因为如果没有头结点,在进行插入和删除操作的时候,插入和删除第一个结点于插入和删除其它结点的操作是不一样的。
三:单链表基本操作
- 单链表实现
- 空链表判断
- 索引位置插入
- 索引位置删除
- 遍历单链表
/*结点类*/
public class myNode{
private Object data; //自定义结点数据类型
public myNode next; //下一个结点
public myNode(Object data,myNode next) { //构造函数
this.data=data;
this.next=next;
}
public Object getData() { //获取结点数据
return data;
}
/* 单链表的实现 */
public class MyLinkedlist {
private myNode head=new myNode(0,null); //定义头结点
private int size=0; //统计结点个数
public boolean Empty() { //空判断
return size<=0;
}
public MyLinkedlist() {}
public void IndexAdd(int index, myNode node) { //索引方式插入结点
myNode temp=head;
int i=0;
if(index<0||index>size) {
System.out.println("添加索引位置错误");
return ;
}
while(temp.next!=null&&i<index) {
temp=temp.next;
i++;
}
node.next=temp.next;
temp.next=node;
size++;
}
public void IndexRemove(int index) { //索引方式删除结点
myNode temp=head;
int i=0;
if(index<0||index>size-1) {
System.out.println("删除索引位置错误");
return ;
}
while(temp.next.next!=null&&i<index){
temp=temp.next;
i++;
}
temp.next=temp.next.next;
size--;
}
public void show() { //遍历结点
if(Empty()) {
System.out.println("链表为空");
return ;
}
myNode temp=head;
while(true) {
if(temp.next==null) {
break;
}
temp=temp.next;
System.out.println(temp.getData());
}
}
/* 测试类 */
public class Test {
public static void main(String[] args) throws Exception {
MyLinkedlist linkedlist=new MyLinkedlist();
myNode node1=new myNode("a1",null); //定义结点
myNode node2=new myNode("a2",null);
myNode node3=new myNode("a3",null);
myNode node4=new myNode("a4",null);
myNode node5=new myNode("a5",null);
linkedlist.IndexAdd(0, node1); //生成链表
linkedlist.IndexAdd(1, node2);
linkedlist.IndexAdd(2, node3);
linkedlist.IndexAdd(3, node4);
linkedlist.IndexAdd(4, node5);
linkedlist.show();
}
}
四:循环单链表
单向循环链表是单链表的另一种形式,其结构特点是链表中最后一个结点的指针不再是结束标记,而是指向整个链表的第一个结点,从而使单链表形成一个环。
和单链表相比,循环单链表的优点找查元素不需要从头开始一个个查找,直接从当前位置一直查询下去即可。当要处理的数据元素序列具有环型结构特点时,适合于采用循环单链表。
和单链表相同,循环单链表也有带头结点结构和不带头结点结构两种,带头结点的循环单链表实现插入和删除操作时,算法实现较为方便。
带头结点的循环单链表的操作实现方法和带头结点的单链表的操作实现方法类同,差别仅在于:
(1)在构造函数中,要加一条head.next = head 语句,把初始时的带头结点的循环单链表设计成上图中(a)所示的状态。
(2)把temp.next!=null 和 temp.next.next != null 和 temp.next ==null 中 null改为head即可实现循环单链表 。
五:有序链表
六:双向链表
双向链表是由前驱指针,数据域,后继指针组成。双向链表与单链表不同之处双向链表查找不需要从起点开始。
双向链表种类:带头结点的双向链表,不带头结点的双向链表。
另外双链表也有单向双链表和循环双链表,循环结构的双向链表用的比较多。
七:双向循环链表
双向循环链表的插入过程:
下图中的指针p表示要插入结点的位置,s表示要插入的结点,①、②、③、④表示实现插入过程的步骤:
循环双向链表的删除过程:
下图中的指针p表示要插入结点的位置,①、②表示实现删除过程的步骤:
八:双向循环链表基本操作
- 双向循环链表的创建
- 双向循环链表的空判
- 双向循环链表的添加
- 双向循环链表的删除
- 双向循环链表的查找
- 双向循环链表的遍历
- 双向循环链表的长度获取
/*循环双链表结点*/
public class DoubleNode {
private Object data;
public Object getData() {
return data;
}
public DoubleNode prior;
public DoubleNode next;
public DoubleNode(DoubleNode prior, Object data, DoubleNode next) {
super();
this.data = data;
this.prior = prior;
this.next = next;
}
}
/*循环双链表实现*/
package Linkedlist;
public class DoubleLinkedlist {
DoubleNode head = new DoubleNode(null, 0, null);
int size = 0;
public DoubleLinkedlist() { //创建链表
head.next = head;
head.prior = head;
}
public boolean empty() { //空链表判断
return size <= 0;
}
public void IndexAdd(int index, DoubleNode node) { //索引位置结点添加
DoubleNode temp = head;
int i = 0;
if (index < 0 || index > size) {
System.out.println("插入索引错误");
return;
}
while (temp.next != head && i < index) {
temp = temp.next;
i++;
}
node.next = temp.next;
temp.next.prior = node;
temp.next = node;
node.prior = temp;
size++;
}
public void indexRemove(int index) { //索引位置结点删除
DoubleNode temp = head;
int i = 0;
if (index < 0 || index > size - 1) {
System.out.println("删除索引位置错误");
return;
}
while (temp.next != head && i <= index) {
temp = temp.next;
i++;
}
temp.prior.next = temp.next;
temp.next.prior = temp.prior;
size--;
}
public void indexSeek(int index) { //索引查找对于元素
DoubleNode temp = head;
int i = 0;
if (index < 0 || index > size - 1) {
System.out.println("查找索引位置错误");
return;
}
while (temp.next != head && i <= index) {
temp = temp.next;
i++;
}
System.out.println("索引为" + index + "的内容是:" + temp.getData());
}
public void show() { //遍历链表
if (empty()) {
System.out.println("链表为空");
return;
}
DoubleNode temp = head;
while (temp.next != head) {
temp = temp.next;
System.out.println(temp.getData());
}
}
public void getLength() { //获取链表长度
System.out.println("链表长度:" + size);
}
}
/* 测试*/
package Linkedlist;
public class Test2 {
public static void main(String[] args) throws Exception {
DoubleLinkedlist linkedlist = new DoubleLinkedlist();
DoubleNode node1 = new DoubleNode(null,"a1", null);
DoubleNode node2 = new DoubleNode(null,"a2", null);
DoubleNode node3 = new DoubleNode(null,"a3", null);
DoubleNode node4 = new DoubleNode(null,"a4", null);
linkedlist.IndexAdd(0, node1);
linkedlist.IndexAdd(1, node2);
linkedlist.IndexAdd(2, node3);
linkedlist.IndexAdd(3, node4);
linkedlist.show();
linkedlist.getLength();
linkedlist.indexSeek(2);
}
}
九:链表中的一些常见问题
- 反转链表
- 检测链表中的循环
- 返回链表倒数第N个节点
- 删除链表中的重复项