数据结构之单链表(java版)

链表(LinkedList)介绍

链表是有序的列表,但是它在内存中是存储如下:
在这里插入图片描述
小结:

  • 链表是以节点的方式来存储,是链式存储
  • 每个节点包含 data 域, next 域:指向下一个节点.
  • 如上图:发现链表的各个节点不一定是连续存储.
  • 链表分带头节点的链表和没有头节点的链表,根据实际的需求来确定

单链表

单链表(带头结点) 逻辑结构示意图如下
在这里插入图片描述

单链表的应用实例

1)使用带head头的单向链表实现 –水浒英雄排行榜管理
2)完成对英雄人物的增删改查操作, 注: 删除和修改,查找可以考虑学员独立完成,也可带学员完成
3)第一种方法在添加英雄时,直接添加到链表的尾部
4)第二种方式在添加英雄时,根据排名将英雄插入到指定位置(如果有这个排名,则添加失败,并给出提示)

第一种添加结点方式的示意图
在这里插入图片描述
思路:
添加(创建)

  1. 先创建一个head 头节点, 作用就是表示单链表的头
  2. 后面我们每添加一个节点,就直接加入到 链表的最后
    遍历:
  3. 通过一个辅助变量遍历,帮助遍历整个链表

代码实现

public class SingleLinkedListDemo {

	public static void main(String[] args) {
		//测试
		//创建结点
		HeroNode hero1=new HeroNode(1, "宋江", "及时雨");
		HeroNode hero2=new HeroNode(2, "卢俊义", "玉麒麟");
		HeroNode hero3=new HeroNode(3, "吴用", "智多星");
		HeroNode hero4=new HeroNode(4, "林冲", "豹子头");
		//创建一个链表
		SingleLinkedList linkedList=new SingleLinkedList();
		//将英雄添加进链表中
		linkedList.add(hero1);
		linkedList.add(hero2);
		linkedList.add(hero3);
		linkedList.add(hero4);
		
		//打印
		linkedList.list();
	}
}
	//创建单链表类
	class SingleLinkedList{
		//创建一个头结点,头结点中不存放任何的数据
		HeroNode head=new HeroNode(0, "", "");
		
		//添加结点,直接向链表尾部添加
		public void add(HeroNode node) {
			//由于头结点不能动,所以我们定义一个临时变量temp指向它
			HeroNode temp=head;
			//循环找到最后一个结点
			while(true) {
				//判断temp的下一个结点是否为空,如果是空的话,我们就找到了最后一个结点,直接退出
				if(temp.next==null) {
					break;
				}
				temp=temp.next;//将指针后移
			}
			//找到最后一个结点之后,我们将数据添加到其后边
			temp.next=node;
		}
		
		//遍历所有结点
		public void list() {
			//用临时变量指向head的下一个结点
			HeroNode temp=head.next;
			//循环遍历
			while(true) {
				//先判断结点是否为空,为空直接退出
				if(temp==null) {
					break;
				}
				//将结点打印出来
				System.out.println(temp.toString());
				//将结点指针后移
				temp=temp.next;
			}
		}
		
		
	}
	
	
	//创建结点类
	class HeroNode{
		int no;  //英雄编号
		String name; //英雄姓名
		String nickname;	//英雄昵称
		HeroNode next;	//指向下一个结点
		//创建构造器,初始化英雄结点
		public HeroNode(int no,String name,String nickname) {
			this.no=no;
			this.name=name;
			this.nickname=nickname;
		}
		
		
		//打印英雄结点信息
		@Override
		public String toString() {
			return "HeroNode [no=" + no + ", name=" + name + ", nickname=" + nickname + "]";
		}
		
	}

控制台打印结果
在这里插入图片描述

第二种添加结点方式的示意图

在这里插入图片描述
思路:
需要按照编号的顺序添加

  1. 首先找到新添加的节点的位置, 是通过辅助变量(指针), 通过遍历来搞定
  2. 新的节点.next = temp.next
  3. 将temp.next = 新的节点

代码实现:
在上述代码的基础上,添加另一个方法

		//添加英雄结点,根据编号顺序进行添加
		public void addByNo(HeroNode node) {
			//因为头结点不能动,所以定义一个临时变量存放头结点
			HeroNode temp=head;
			//定义一个flag,用于判断添加的编号是否存在
			boolean flag=false;
			//循环遍历结点
			while(true) {
				//遍历到最后一个结点,退出
				if(temp.next==null) {
					break;
				}else if(temp.next.no>node.no) {
					//当下一个结点的no大于我们所要插入的结点编号,
					//这时候我们就可以将要插入的结点放在temp后
					//找到该要插入的结点,退出
					break;
				}else if(temp.next.no==node.no) {//如果编号相等,那就另flag==true
					flag=true;
				}
				
				//将指针后移
				temp=temp.next;
			}
			
			//插入前我们需要判断链表中是否有该no
			if(flag==true) {
				System.out.printf("不能插入,链表中已经存在%d号英雄的信息",node.no);
				System.out.println();
			}else {
				node.next=temp.next;
				temp.next=node;
			}
		}

主类实现,可以乱序插入

public static void main(String[] args) {
		//测试
		//创建结点
		HeroNode hero1=new HeroNode(1, "宋江", "及时雨");
		HeroNode hero2=new HeroNode(2, "卢俊义", "玉麒麟");
		HeroNode hero3=new HeroNode(3, "吴用", "智多星");
		HeroNode hero4=new HeroNode(4, "林冲", "豹子头");
		//创建一个链表
		SingleLinkedList linkedList=new SingleLinkedList();
		//将英雄添加进链表中
		linkedList.addByNo(hero1);
		linkedList.addByNo(hero4);
		linkedList.addByNo(hero3);
		linkedList.addByNo(hero2);
		//添加一个已存在的结点
		linkedList.addByNo(hero3);
		//打印
		linkedList.list();
	}

控制台打印结果
在这里插入图片描述

根据英雄no修改英雄结点信息

在上述代码中添加下述方法
代码实现

//根据结点的no修改结点信息,即no属性不能动
		public void update(HeroNode newHeroNode) {
			//首先判断链表是否为空
			if(head.next==null) {
				System.out.println("空链表!!!");
				return ;				
			}
			//定义一个flag,标志是否找到
			boolean flag=false;
			//定义一个临时变量
			HeroNode temp=head.next;
			while(true) {
				//判断是否到链表结尾
				if(temp==null) {
					break;
				}else if(temp.no==newHeroNode.no) {//找到要修改的结点
					flag=true;//将 标志修改
					break;
				}
				temp=temp.next;//指针后移
			}
			//判断是否找到
			if(flag) {
				temp.name=newHeroNode.name;
				temp.nickname=newHeroNode.nickname;
			}else {
				System.out.printf("没有找到编号为%d的英雄信息",newHeroNode.no);
				System.out.println();
			}
			
		}

主类测试代码中添加

		//测试修改英雄结点信息
		HeroNode newHeroNode=new HeroNode(2, "小卢", "玉麒麟~~");
		System.out.println("修改后的链表信息~");
		linkedList.update(newHeroNode);
		linkedList.list();

控制台输出
在这里插入图片描述

根据英雄no删除结点信息

示意图

在这里插入图片描述
思路:

从单链表中删除一个节点的思路

  1. 我们先找到 需要删除的这个节点的前一个节点 temp
  2. temp.next = temp.next.next
  3. 被删除的节点,将不会有其它引用指向,会被垃圾回收机制回收

代码实现:
在上述代码中添加:

		//删除指定no英雄结点信息
		/**
		 * 思路:
		 * 1、首先假设我们找到了要删除的结点,我们无法将该节点进行删除,
		 * 	所以我们需要找到要删除结点的头一个结点,才能删除结点信息
		 * 2、我们修改要删除结点的头一个结点指针的指向,使他指向要删除结点的下一个结点
		 * 		temp.next=temp.next.next
		 * 
		 * @param no
		 */
		public void delete(int no) {
			//定义一个临时变量
			HeroNode temp=head;
			boolean flag=false;//是否找到要删除结点的头一个结点的标志
			while(true) {
				//判断是否到链表尾部
				if(temp.next==null) {
					break;
				}else if(temp.next.no==no) {//找到该结点的上一个结点
					flag=true;
					break;
				}
				//指针后移
				temp=temp.next;
			}
			//判断是否找到要删除结点的头一个结点
			if(flag) {
				temp.next=temp.next.next;
			}else {
				System.out.printf("没有找到编号为%d的英雄信息",no);
			}
			
		}

主类测试代码

在主类测试代码中加入

		//测试结点删除
		System.out.println("删除后的结点信息~");
		linkedList.delete(1);
		linkedList.delete(4);
		linkedList.list();

控制台输出信息

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值