没有头节点单链表:也就是phead只是一个引用(指针),指向链表的第一个节点
有头节点单链表:phead是头节点只不过头节点的data不保存信息
基本概念:链表的每个元素称为一个节点,每个节点都可以存储在内存中的不同的位置,为了表示每个元素与后继元素的逻辑关系,以便构成“一个节点链着一个节点”的链式存储结构,除了存储元素本身的信息外,还要存储其直接后继信息,因此,每个节点都包含两个部分,第一部分称为链表的数据区域,用于存储元素本身的数据信息,这里用data表示,它不局限于一个成员数据,也可是多个成员数据,第二部分是一个结构体指针,称为链表的指针域,用于存储其直接后继的节点信息,这里用next表示,next的值实际上就是下一个节点的地址,当前节点为末节点时,next的值设为空指针
数组:数组(包括结构体数组)的实质是一种线性表的顺序表示方式,它的优点是使用直观,便于快速、随机地存取线性表中的任一元素,但缺点是对其进行 插入和删除操作时需要移动大量的数组元素,同时由于数组属于静态内存分配,定义数组时必须指定数组的长度,程序一旦运行,其长度就不能再改变,实际使用个数不能超过数组元素最大长度的限制,否则就会发生下标越界的错误,低于最大长度时又会造成系统资源的浪费,因此空间效率差。
链表:链表实际上是线性表的链式存储结构,与数组不同的是,它是用一组任意的存储单元来存储线性表中的数据,存储单元不一定是连续的,且链表的长度不是固定的,链表数据的这一特点使其可以非常的方便地实现节点的插入和删除操作。链表的特性,使其在某些操作上比数组更加高效。例如当进行插入和删除操作时,链表操作的时间复杂度仅为O(1)。另外,因为链表在内存中不是连续存储的,所以可以充分利用内存中的碎片空间。除此之外,链表还是很多算法的基础,最常见的哈希表就是基于链表来实现的。
本节将具体介绍单向链表(带头结点)的结构和各种操作的具体实现
为了方便对链表各项操作的理解,把链表应用到具体的实例中:用带头节点的单链表实现学生成绩管理系统。对学生的成绩信息实现增删改查的操作,具体实现目标如下:
package com.zhukun.LinkList;
import java.util.Scanner;
class StudentLinkNode
{
public int stuno;//学生学号
public String name;//学生姓名
public int score;//学生分数
public StudentLinkNode next;//指向下一个结点的指针
//构造器
public StudentLinkNode() {}
public StudentLinkNode(int stuno,String name,int score) {
this.stuno = stuno;
this.name = name;
this.score = score;
}
public void setInfo()
{
Scanner scanner = new Scanner(System.in);
System.out.print("学号:");
this.stuno = scanner.nextInt();
System.out.print("姓名:");
this.name = scanner.next();
System.out.print("分数:");
this.score = scanner.nextInt();
}
//为了显示方法,重写toString方法
public String toString() {
return "学号: "+stuno+" "+"姓名"+name+" "+"分数"+score;
}
}
class LinkedList{
//先初始化一个头节点,头节点不要动,不存放具体的数据
private StudentLinkNode head = new StudentLinkNode(0,"",0);
//添加结点到单向链表
//当不考虑编号顺序时
//1、找到当前链表的最后结节点
//2、将最后这个节点的next 指向 新的结点
public void add(StudentLinkNode stuNode)
{
//因为head节点不能动,因此我们需要一个辅助遍历的节点 temp
StudentLinkNode temp = head;
while(true) {
//找到链表的最后的节点
if(temp.next == null)
{
//找到
break;
}
//如果遍历的当前结点不是尾结点,将temp节点向后移动
temp = temp.next;
}
//当退出while循环时,表示temp就指向了链表的尾节点
temp.next = stuNode;
}
//第二种添加方式,在添加学生时根据学号将学生插入到指定位置
public void addBystuno(StudentLinkNode stuNode)
{
//因为头节点不能动,因此我们仍然通过一个辅助指针(变量)来帮助找到添加的位置
//因为是单链表,我们找的temp是位于添加位置的前一个节点,否则插入不了
StudentLinkNode temp = head;
boolean flag = false;//flag标志添加的学号是否存在,默认为false
while(true)
{
if(temp.next == null) //说明temp已经在链表的最后
{
break;
}
if(temp.next.stuno > stuNode.stuno)//位置找到,就在temp的后面插入
{
break;
}
else if(temp.next.stuno == stuNode.stuno)//说明想要添加的
{
flag = true;//说明编号存在
break;
}
temp = temp.next;//后移
}
//判断flag的值
if(flag)
{ //不能添加,说明编号存在
System.out.println("准备添加的学生的学号:"+stuNode.stuno+"已经存在不能添加了");
}else {
//插入到链表中,temp的后面
stuNode.next = temp.next;
temp.next = stuNode;
}
}
//遍历链表
public void showlist() {
//判断链表是否为空
if(head.next == null)
{
System.out.println("链表为空");
return;
}
//因为头节点,不能动,因此我们需要一个辅助变量来遍历
StudentLinkNode temp = head.next;
while(true)
{
//判断是否到链表最后
if(temp == null)
{
break;
}
//输出节点的信息
System.out.print(temp);//前面StudentLinkNode类中已经重写toString方法
System.out.println();
//将temp后移
temp = temp.next;
}
}
//返回链表中共有多少个学生的信息
public int len()
{
int count=0;
//判断链表是否为空
if(head.next == null)
{
System.out.println("链表为空");
return 0;
}
//因为头节点,不能动,因此我们需要一个辅助变量来遍历
StudentLinkNode temp = head.next;
while(true)
{
//判断是否到链表最后
if(temp == null)
{
break;
}
//输出节点的信息
count++;
//将temp后移
temp = temp.next;
}
return count;
}
//删除结点:根据学生学号删除指定学生节点
//1、head不能动,因此我们需要一个temp辅助节点找到待删除节点的前一个节点
//2、说明我们在比较时,是temp.next.stuno和需要删除的节点的stuno比较
public void del(int stuno)
{
StudentLinkNode temp = head;
boolean flag =false;//标志是否找到待删除的节点
while(true)
{
if(temp.next == null)
{
break;//已经到链表的最后
}
if(temp.next.stuno == stuno)
{
//找到待删除的节点的前一个节点temp
flag = true;
break;
}
temp = temp.next;//temp后移,遍历
}
//判断flag
if(flag)
{//找到
//可以删除
temp.next = temp.next.next;
}else {
System.out.println("学号"+stuno+"的学生不存在");
}
}
//修改节点信息,根据stuno 编号来修改
public void update(StudentLinkNode stuNode)
{
//判断是否为空
if(head.next == null)
{
System.out.println("链表为空");
return;
}
//找到需要修改的节点,根据stuno编号
//定义一个辅助变量
StudentLinkNode temp = head.next;
boolean flag =false;//表示是否找到该节点
while(true)
{
if(temp == null)
{
break;//已经遍历完链表
}
if(temp.stuno == stuNode.stuno)//找到
{
flag = true;
break;
}
temp = temp.next;
}
//根据flag判断是否找到要修改的节点
if(flag) {
temp.name = stuNode.name;
temp.score = stuNode.score;
}else {
System.out.println("没有找到学号为"+stuNode.stuno+"的学生");
}
}
//查询指定分数的学生个数,并输出查询的学生信息
public void getbyScore(int score)
{
StudentLinkNode temp = head;
int count=0;
System.out.println("分数为"+score+"的学生有:");
while(temp != null)
{
if(temp.score == score)
{
count++;
System.out.println("学号:"+temp.stuno+"姓名:"+temp.name);
}
temp = temp.next;
}
System.out.println("共上面"+count+"个");
}
}
public class LinkListDemo {
public static void main(String[] args)
{
LinkedList list = new LinkedList();
int n;//接收用户输入
Scanner scanner = new Scanner(System.in);
boolean loop = true;
//输出一个菜单
while(loop)
{
System.out.println("|-----欢迎来到学生成绩管理系统-----|");
System.out.println("1、添加学生成绩信息");
System.out.println("2、根据学号将学生信息插入到指定位置");
System.out.println("3、显示所有学生的信息");
System.out.println("4、删除指定学生成绩信息");
System.out.println("5、修改指定学生成绩信息");
System.out.println("6、查询当前链表中有多少为同学");
System.out.println("7、查询指定分数的学生个数,并输出查询的学生信息");
System.out.println("8、退出程序");
System.out.println("请输入你的选择:");
n = scanner.nextInt();
switch(n)
{
case 1:
System.out.println("请输入学生信息");
StudentLinkNode s1=new StudentLinkNode();
s1.setInfo();
list.add(s1);
break;
case 2:
System.out.println("请输入学生信息");
StudentLinkNode s2=new StudentLinkNode( );
s2.setInfo();
list.addBystuno(s2);
break;
case 3:
list.showlist();
break;
case 4:
System.out.print("请输入你要删除的学生学号");
int stuno = scanner.nextInt();
list.del(stuno);
System.out.println("删除成功");
break;
case 5:
System.out.println("请输入你要修改的学生的学号:");
int sno = scanner.nextInt();
System.out.println("请输入修改后的姓名为:");
String sname = scanner.next();
System.out.println("请输入修改后的分数为:");
int sco = scanner.nextInt();
StudentLinkNode s3 = new StudentLinkNode(sno,sname,sco);
list.update(s3);
System.out.println("修改成功");
break;
case 6:
int num = list.len();
System.out.println("当前链表中共记录了"+num+"位学生成绩信息");
break;
case 7:
System.out.println("请输入你要查询的分数:");
int sco2 = scanner.nextInt();
list.getbyScore(sco2);
break;
case 8:
scanner.close();
loop = false;
break;
}
}
}
}