数据结构 - 单链表
数据结构 - 单链表
1 使用Java实现单链表
-
自定义Node节点类,该类包含:
- 节点内存储的数据内容Data
- 该节点指向的下一个节点指针Next
- 无参(用来构建头节点)和有参的构造方法(用来给链表传输数据)
-
自定义一个SingleLinkedList类,该类包含:
- 单向链表头节点HeadNode,该头节点在SingleLinkedList类调用无参方法时进行初始化(节点内Data为null)
- 链表的长度Length
- HeadNode的get方法(用于后面测试类中可以获取到头节点)
- Length的get和set方法(用于后面测试类中获取链表的长度)
- 三种插入方法(addFirst头插法,addLast尾插法,addByIndex指定位置插入)
- 按位置查找FindByIndex
- 按元素值查找FindByElement
- 按指定位置删除DeleteByIndex
- 打印链表所有数据ListPrint
- 链表长度listSize
public class SinglyLinkedList<E> {
static class Node<E> {
E Data;
Node<E> Next;
public Node() {
this.Data = null;
}
public Node(E data) {
Data = data;
}
}
private Node HeadNode;
private int Length = 0;
public Node getHeadNode() {
return HeadNode;
}
public int getLength() {
return Length;
}
public void setLength(int length) {
Length = length;
}
public SinglyLinkedList() {
this.HeadNode = new Node<E>();
}
/*
插入操作,分为三种情况:
1. 头插法 - addFirst - 时间复杂度O(1)
2. 尾插法 - addLast - 时间复杂度O(n)
3. 指定位置插入 - addByIndex - 平均时间复杂度O(n/2) - O(n)
*/
//头插法
public void addFirst(E e){
//创建一个Node节点并把数据放入Data
Node<E> data = new Node(e);
//插入节点的Next设置为Head节点的Next
data.Next = this.HeadNode.Next;
//Head节点的Next设置为插入节点
this.HeadNode.Next = data;
//更新链表长度
this.Length++;
}
//尾插法
public void addLast(E e) {
//创建一个Node节点并把数据放入Data
Node<E> data = new Node(e);
//判断该链表长度是否为0,如果为0,则直接在头节点后插入该节点
if (this.Length == 0){
data.Next = this.HeadNode.Next;
this.HeadNode.Next = data;
}
//如果不是头节点,则需要先找到链表最后一个节点再插入
else {
//创建一个temp节点用来当作移动指针,因为链表长度不为0,temp节点初始位置为头节点后第一个节点
Node<E> tem = this.HeadNode.Next;
//当移动指针的下一个节点不为空时,指针向后走一个
//直到移动指针的下一个节点为空,此时temp为最后一个节点
while (tem.Next != null){
tem = tem.Next;
}
//插入Node的下一个节点设置为空,因为该节点为链表新尾节点
data.Next = null;
//设置移动指针(当前链表的尾节点)的下一个节点为插入节点
tem.Next = data;
}
//插入成功后,更新链表长度
this.Length++;
}
//指定位置插入
public void addByIndex(E e,int index){
//先判断输入位置是否合法,插入位置范围为[1,Length+1]
if (index >= 1 && index <= this.Length+1) {
//data节点用来接收数据
Node<E> data = new Node<>(e);
//temp节点用来当作移动指针
Node<E> temp = this.HeadNode;
int len = 0;
//找到插入位置的前一个结点
while (len < index - 1) {
temp = temp.Next;
len++;
}
data.Next = temp.Next;
temp.Next = data;
this.Length++;
} else {
System.out.println("插入位置异常!");
}
}
/*
查找链表中第i个数据的元素值
*/
public E FindByIndex(int i){
int len = 1;
//确保输入位置不会越界并且不会为0或者负数
if (i > this.Length || len > i){
System.out.println("输入异常!");
return null;
}
//定义移动指针
Node<E> temp = this.HeadNode.Next;
while (len < i){
temp = temp.Next;
len++;
}
return temp.Data;
}
/*
按值查找链表中对应元素的
*/
public int FindByElement(E e){
Node<E> temp = this.HeadNode.Next;
int len = 1;
//从第一个节点开始判断节点中的数据和输入的是否相当
while (temp.Data != e){
if (len == this.Length){
len = -1;
break;
}
temp = temp.Next;
len++;
}
return len;
}
/*
按指定位置删除
*/
public void DeleteByIndex(int index){
if (index >= 1 && index <= this.Length){
int len = 0;
Node<E> temp = this.HeadNode;
//找到删除节点的前一个结点
while (len < index-1){
temp = temp.Next;
len++;
}
temp.Next = temp.Next.Next;
this.Length--;
}else {
System.out.println("删除位置异常!");
}
}
/*
打印链表中所有数据
*/
public void ListPrint(){
Node<E> temp = this.HeadNode.Next;
while (temp != null){
System.out.print(temp.Data+" ");
temp = temp.Next;
}
System.out.println();
}
public int listSize(){
return this.Length;
}
}
2 单链表练习题
2.1 链表合并
将两个有序(非递减)单链表La和Lb合并成为一个有序(非递减)单链表
非递减:允许相等的递增
public class ListCombine {
/*
将两个有序(非递减)单链表La和Lb合并成为一个有序(非递减)单链表
非递减:允许相等的递增
解题思路:
链表的合并不需要再创建新空间,只需要把两个链表中的节点按非递减顺序串联起来
单链表头指针不可移动
*/
public static void MergeList(SinglyLinkedList<Integer> La, SinglyLinkedList<Integer> Lb){
//temp为移动指针,用来连接合并后的新链表
SinglyLinkedList.Node<Integer> temp = La.getHeadNode();
//p负责将La链表串起来
SinglyLinkedList.Node<Integer> p = La.getHeadNode().Next;
//q负责将Lb链表串起来
SinglyLinkedList.Node<Integer> q = Lb.getHeadNode().Next;
//合并后新链表的长度为原来两链表长度之和
int len = La.listSize()+Lb.listSize();
//当p和q节点都不为空时,即La和Lb都没有遍历完成时
while (p!=null && q!=null) {
//判断p和q的大小,temp指向小的那个节点,小的那个节点向后移一位
if (p.Data <= q.Data) {
temp.Next = p;
temp = temp.Next;
p = p.Next;
} else {
temp.Next = q;
temp = temp.Next;
q = q.Next;
}
}
//当p和q中有一个节点为空时,终止循环
//此时判断那个节点为空,temp指向非空的那个节点
temp.Next = p!=null?p:q;
La.setLength(len);
}
//测试类
public static void main(String[] args) {
SinglyLinkedList<Integer> la = new SinglyLinkedList<>();
SinglyLinkedList<Integer> lb = new SinglyLinkedList<>();
la.addLast(3);
la.addLast(8);
la.addLast(9);
la.addLast(15);
la.addLast(20);
lb.addLast(5);
lb.addLast(7);
lb.addLast(13);
lb.addLast(15);
lb.addLast(19);
lb.addLast(22);
la.ListPrint();
lb.ListPrint();
MergeList(la,lb);
la.ListPrint();
System.out.println(la.listSize());
}
}
2.2 查找中间节点
带有头节点的单链表L,设计一个高效的算法求L的中间节点
public class MidSearchList {
/*
带有头节点的单链表L,设计一个高效的算法求L的中间节点
解题思路:
快慢指针,快指针一次走两步,慢指针一次走一步,当快指针走向末尾时慢指针刚好到中点
*/
public static SinglyLinkedList.Node MidNodeSearch(SinglyLinkedList<Integer> L){
SinglyLinkedList.Node f = L.getHeadNode();
SinglyLinkedList.Node s = L.getHeadNode();
//循环条件快指针不为0放前面,因为当链表长度为奇数时快指针最后为空,f.next会报空指针异常
while (f!=null && f.Next!=null){
f = f.Next.Next;
s = s.Next;
}
return s;
}
public static void main(String[] args) {
SinglyLinkedList<Integer> l = new SinglyLinkedList<>();
l.addLast(2);
l.addLast(3);
l.addLast(4);
l.addLast(5);
l.addLast(6);
System.out.println(MidNodeSearch(l).Data);
}
}
2.3 链表绝对值去重
设计一个高时间复杂度的算法,对链表中data的绝对值相等的节点
仅保留第一次出现的节点而删除其余绝对值相等的节点
import java.util.HashMap;
public class RemoveDuplicates {
/*
设计一个高时间复杂度的算法,对链表中data的绝对值相等的节点
仅保留第一次出现的节点而删除其余绝对值相等的节点
*/
//自定义一个absolute方法取绝对值
public static int absolute(int num){
return num >= 0 ? num : -num;
}
public static void listDuplicatesRemove(SinglyLinkedList L){
//用map来存链表中数据出现过的绝对值
HashMap<Integer,Integer> map = new HashMap<>();
//temp用来连接新链表
SinglyLinkedList.Node temp = L.getHeadNode();
//p用来串起旧链表
SinglyLinkedList.Node<Integer> p = L.getHeadNode().Next;
int len = 0;
//遍历原来链表
while (p!=null){
//如果map里有绝对值重复的key直接跳过
//如果没有则将该绝对值放进map并让temp指向该p节点
if (!map.containsKey(absolute(p.Data))){
map.put(absolute(p.Data),1);
temp.Next = p;
temp = temp.Next;
len++;
}
p = p.Next;
}
//让新链表的尾节点指向空,因为可能出现该节点与之后节点绝对值相等的情况
temp.Next = null;
L.setLength(len);
}
public static void main(String[] args) {
SinglyLinkedList<Integer> l = new SinglyLinkedList<>();
l.addLast(2);
l.addLast(3);
l.addLast(3);
l.addLast(5);
l.addLast(-2);
l.addLast(2);
l.addLast(6);
l.addLast(6);
l.addLast(3);
l.addLast(7);
l.addLast(1);
l.addLast(-8);
l.addLast(8);
l.ListPrint();
listDuplicatesRemove(l);
l.ListPrint();
System.out.println(l.listSize());
}
}
3 Java实现静态链表
public class staticSingleLinkedList {
/*
自定义静态单向链表
*/
//表示链表的实际长度 - 即链表中保存了几个数据
private int listLen = 0;
//用户输入的初始化链表长度 - 数组可用的长度
private int Length;
//用来记录Data数组存到了哪个位置
//Data数组存放数据按顺序放入,size自增
private int size=0;
//存放数据
private Object[] Data;
//存放位置
private int[] Right;
//有参构造,给定链表初始长度值
//初始化链表长度,存数据的Data数组,保存位置的Right数组
public staticSingleLinkedList(int length) {
Length = length;
Data = new Object[length];
Right = new int[length];
}
public void addByLast(Object obj){
//判断Data数组容量是否达到80%,如果是则扩容
//减一是因为Data数组存入数据从下标1开始
if (size >= Length*0.8-1){
arrayExpanding(size);
}
//因为Data数组下标0的位置表示头节点不存数据,数据从下表1开始存入
Data[size+1] = obj;
//将前Data数组中前一个节点size指向插入节点size+1
Right[size] = size+1;
//更新Data数组插入位置
size++;
listLen++;
}
public void addByFirst(Object obj){
//判断是否需要扩容
if (size >= Length*0.8-1){
arrayExpanding(size);
}
//Data数组按顺序插入
Data[size+1] = obj;
//因为为头插法,新插入数据放在头节点即Right[0]后
//新插入节点的下一个节点为原来0位置节点的下一个节点
Right[size+1] = Right[0];
//0位置节点的下一个节点为新插入节点
Right[0] = size+1;
size++;
listLen++;
}
public void addByIndex(int index,Object obj){
//判断输入下标是否合理
if (index<1||index>listLen+1){
System.out.println("下标输入错误");
return;
}
//判断是否需要扩容
if (size >= Length*0.8-1){
arrayExpanding(size);
}
Data[size+1] = obj;
//找到插入位置节点的前一个结点,从头节点开始
int j = 0;
for (int i = 1; i < index; i++) {
j = Right[j];
}
//新插入节点指向插入位置的节点即找到的前一个节点的后一个节点
Right[size+1] = Right[j];
//插入节点的前一个节点指向新插入节点
Right[j] = size+1;
size++;
listLen++;
}
//根据输入下标查找
public Object searchByIndex(int index){
//判断输入位置是否合理
if (index<1||index>listLen){
throw new IndexOutOfBoundsException("下标越界!");
}
//找到输入下标位置的前一个结点
int j = 0;
for (int i = 1; i < index; i++) {
j = Right[j];
}
//返回查找位置的值 - Right[j]为找到的前一个节点的后一个节点即查找位置节点
return Data[Right[j]];
}
//按元素值查找
public int searchByData(Object obj){
//遍历循环整个链表
int j = 0;
for (int i =0; i < listLen; i++) {
//如果找到该元素则返回结束
if (Data[Right[j]] == obj){
return i+1;
}else {
//值不相同则接着比较下一个元素
j = Right[j];
}
}
//遍历完了都没有找到则不存在,反回-1
return -1;
}
public void deleteByIndex(int index){
//判断输入位置是否合理
if (index<1||index>listLen){
throw new IndexOutOfBoundsException("下标越界!");
}
int j = 0;
//扎到删除节点的前一个结点
for (int i = 1; i < index; i++) {
j = Right[j];
}
//删除节点的前一个节点直接指向删除节点的后一个节点
Right[j] = Right[Right[j]];
//链表长度减一
listLen--;
}
public void listPrint(){
int j = 0;
for (int i = 1; i <= listLen; i++) {
System.out.print(Data[Right[j]]+" ");
j = Right[j];
}
System.out.println();
}
//数组自动扩容
//新建两个等长的数组arr,arr1拷贝Data,Right的数据
//对Data,Right扩容1.5倍并重新初始化
//再将arr,arr1的数据拷贝回扩容后的数组
public void arrayExpanding(int count){
Object[] arr = new Object[count+1];
System.arraycopy(Data,0,arr,0,count+1);
Data = new Object[Data.length+(Data.length>>1)];
System.arraycopy(arr,0,Data,0,count+1);
int[] arr1 = new int[count+1];
System.arraycopy(Right,0,arr1,0,count+1);
Right = new int[Data.length+(Data.length>>1)];
System.arraycopy(arr1,0,Right,0,count+1);
Length = Data.length;
}
public static void main(String[] args) {
staticSingleLinkedList list = new staticSingleLinkedList(5);
list.addByLast(1);
list.addByLast(2);
list.addByLast(3);
list.addByLast(4);
list.addByLast(5);
list.addByLast(6);
list.addByFirst(7);
list.listPrint();
list.addByIndex(2,8);
list.listPrint();
list.addByIndex(1,9);
list.listPrint();
list.addByIndex(9,10);
list.listPrint();
list.addByIndex(11,11);
list.listPrint();
//System.out.println(list.searchByIndex(12));
//System.out.println(list.searchByData(15));
list.deleteByIndex(3);
list.listPrint();
list.deleteByIndex(1);
list.listPrint();
list.deleteByIndex(9);
list.listPrint();
}
}

浙公网安备 33010602011771号