数据结构之单链表
1.单链表的定义及结构
单链表是一种链式存取的数据结构,用一组地址任意的存储单元存放线性表中的数据元素。链表中的数据是以结点来表示的,每个结点的构成:元素域 + 指针域,元素就是存储数据的存储单元,指针就是连接每个结点的地址数据。
头指针:
在线性表的链式存储结构中,头指针是指链表指
向第一个结点的指针,若链表有头结点,则头指
针就是指向链表头结点的指针。
2.链表节点的实现
//定义一个HeroNode,每个HeroNode对象就是一个节点
class HeroNode {
//表示链表的数据域
public int no;
//表示链表的指针域,用于指向下一个节点
public HeroNode next;
public HeroNode(int no, String name, String nickname) {
this.no = no;
this.name = name;
this.nickname = nickname;
}
//重写toString方法
@Override
public String toString() {
return "HeroNode{" +
"no=" + no +
", name='" + name + '\'' +
", nickname='" + nickname + '\'' +
'}';
}
}
3.定义一个单链表
//定义一个单链表
class SingleLinkedList{
//初始化一个头节点,一般不动
private HeroNode head = new HeroNode(0,"","");
//用于获取head这个私有属性
public HeroNode getHead() {
return head;
}
}
4.利用头插法插入元素
//采用头插法
public void addHead(HeroNode node){
if(head == null){
return;
}
HeroNode temp ;
temp = head.next;
head.next=node;
node.next = temp;
}
5.利用尾插法插入元素
//尾插法插入元素
public void add(HeroNode heroNode){
//需要辅助节点temp
HeroNode temp = head;
//遍历链表找到最后一个节点
while(true){
if(temp.next==null){
break;
}
temp = temp.next;
}
temp.next= heroNode;
}
6.利用中间插入法插入元素
//利用中间插入法插入元素
public void addOrder(HeroNode heroNode){
//因为是单链表,因此temp是位于添加位置的前一个节点
HeroNode temp = head;
boolean flag = false;
while(true){
if(temp.next == null){
break;
}
if(temp.next.no>heroNode.no){
break;
}else if(temp.next.no == heroNode.no){
flag = true;//说明编号存在
break;
}
temp = temp.next;
}
if(flag){
System.out.println("准备插入的英雄编号已经存在");
}
else{
heroNode.next = temp.next;
temp.next = heroNode;
}
}
7.删除链表中的节点
//删除节点
public void remove(HeroNode heroNode){
HeroNode temp = head;
boolean flag = false;
while(true){
if(temp.next==null){
break;
}
if(temp.next.no == heroNode.no ){
flag = true;
break;
}
temp = temp.next;
}
if(flag){
temp.next=temp.next.next;
}else{
System.out.println("没有找到要删除的节点");
}
}
8.打印单链表
//打印链表
public void list(){
//判断链表是否为空
if(head.next==null){
System.out.println("链表为空");
return;
}
HeroNode temp = head.next;
while(true){
if(temp==null){
break;
}
System.out.println(temp);
temp = temp.next;
}
}
8.获取链表中节点的个数
//获取单链表的节点的个数,如果是带头节点的链表,不包含头节点
public static int getLength(HeroNode head){
if(head.next == null){
return 0;
}
int length = 0;
HeroNode cur = head.next;
while(cur != null){
length++;
cur = cur.next;
}
return length;
}
}
9.查看链表中倒数第k个节点
//查找单链表的倒数第k个节点
public static HeroNode findLastNode(HeroNode head,int index){
if(head.next == null){
return null;
}
int size = getLength(head);
if(index<=0 || index > size){
return null;
}
HeroNode cur = head.next;
for(int i = 0; i < size-index;i++){
cur = cur.next;
}
return cur;
}
10.对单链表进行翻转
//翻转链表
public static void reverse(HeroNode head){
//当链表为空或者链表只有一个元素时不能进行翻转
if(head.next == null||head.next.next==null){
return;
}
HeroNode cur = head.next;//当前节点
HeroNode next = null;//next是指向当前节点的下一个节点
HeroNode reverseNode = new HeroNode(0,"","");
while(cur != null){
next = cur.next;
cur.next = reverseNode.next;
reverseNode.next=cur;
cur = next;
}
//将head.next指向reversnNode.next
head.next = reverseNode.next;
}
11.从尾到头打印单链表
public void printFan(HeroNode head){
HeroNode node1 = head;
if(node1 == null)return;
printFan(node1.next);
System.out.println(node1.no+" ");
}
12.合并两个有序的链表
//合并两个有序链表
public static HeroNode mergeLinked(HeroNode node1,HeroNode node2){
if(node1 == null) return node2;
if(node2 == null) return node1;
HeroNode newNode = new HeroNode(0,"","");
if(node1.no <= node2.no){
newNode = node1;
newNode.next=mergeLinked(node1.next,node2);
}else{
newNode = node2;
newNode.next = mergeLinked(node1,node2.next);
}
return newNode;
}
13.链表的优缺点
优点:
1.动态数据结构
链表是一种动态数据结构,可以在运行时通过
分配和取消分配内存来增长和缩小。所以没有必
要给出链表的初始大小
2.易于插入和删除
与数组不同,我们不必在插入或删除元素后移位
元素。在链表中,我们只需要更新节点下一个指
针中的地址。
3.内存利用率高
链表的大小可以在运行时增加或减少,因此没有
内存浪费。在数组的情况下,存在大量的内存浪
费,就像我们声明一个大小为10的数组并且只存
储6个元素,那么浪费了4个元素的空间。链表中
没有这样的问题,因为只在需要时才分配内存。
缺点:
1.内存的使用
与数组相比,在链表中存储元素需要更多内存。
因为在链表中每个节点都包含一个指针,它需要
额外的内存。
2.遍历困难,不易查询
链表中的元素或节点遍历很困难,访问元素的效
率低。我们不能像索引一样随机访问任何元素。
例如,如果我们想要访问位置n的节点,那么我
们必须遍历它之前的所有节点。
------------------------------------------------------------