Java数据结构之线性表细解(免费附赠源码) | CSDN创作打卡

2.1 线性表的逻辑概念

2.1 线性表的概念
1、线性表的定义
线性表L:是由n(n≥0)个数据元素(结点)a1,a2, …an组成的有限序列。其中数据元素的个数n定义为表的长度,当n=0时称为空表。
线性表记作:
L= (a1,a2,…an) (n>=0)
数据元素ai(1≤i≤n),其前驱元素为ai-1,其后继元素为 ai+1 。
线性表的每个元素,可以是简单数据,也可以是构造数据。含有大量数据记录的线性表就是文件。 例如、学生健康情况登记表
(说明:在Java中,可以用类定义数据元素的数据类型)

2、线性表的逻辑特征:
(1) 对非空的线性表,除第1个元素和最后1个元素外,每个元素只有一个直接前驱和直接后继。
(2) 线性表中所有元素,其数据类型相同。
(3) 线性表中数据元素是有序的。

2.2 线性表的基本运算(或基本操作):

2.2 线性表的基本运算(或基本操作):
(1) 建立空的线性表SetNullList()
(2) 求线性表的长度Len(),返回整数
(3) 取第i个元素的结点 IndexOf(i):有这个节点,返回此元素,否则返回空
(4) 查找结点,返回引用或序号 Find(key):找不到,返回-1或空引用
(5) 插入(在指定位置插入一个结点)Insert(i,node) :在线性表指定地方插入一个元素,插入成功返回true,失败返回false
(6) 追加Append(node) :在线性表的表尾插入一个元素,插入成功返回true,失败返回false
(7) 删除(删除第i个结点)Delete(i):若成功返回true,失败返回false
(8) 判表是否空 Empty()
(9) 遍历线性表VisitAll():遍历线性表,返回数据
(说明,在Java中,可以用接口来定义线性表的上述操作集合)

2.3 线性表的顺序表示和实现

2.3 线性表的顺序存储结构――顺序表
指用一组地址连续的存储单元,依次存储线性表中的数据元素,具体的说,就是用数组来存储顺序表。
其特征是,逻辑上相邻的元素结点,物理存储位置上也相邻。

1、顺序表的算法实现
(1) 建立空的线性表SetNullList(),顺序表不需此算法
(2) 求线性表的长度Len (),返回整数
(3) 取第i个元素的结点IndexOf (i):有此节点,返回此元素对象,否则返回空
(4) 查找结点,返回序号Find (key):找不到,返回-1
(5) 插入(在指定位置插入一个结点)Insert(i,node) :在顺序表指定地方插入一个元素,如果已满则插入失败,插入成功返回true,失败返回false
(6) 追加Append(node) :在顺序表的表尾插入一个元素,如果已满则插入失败,插入成功返回true,失败返回false
(7) 删除(删除第i个结点)Delete(i):若成功返回true,失败返回false
(8) 判表是否空 Empty()
(9) 遍历线性表VisitAll():遍历顺序表,返回数据

下面单独对(5) 插入(在指定位置插入一个结点)Insert(i,node)说明算法思想:
(1)判断顺序表的存储空间是否已满,或满结束并返回false。
(2)判断插入位置i 是否合法,或不合法结束并返回false。
(3)判断插入位置i是否是表尾,若是,不用移动元素,或不是,将第i 位开始的元素依次向后移动一个位置,空出第i个位置。
(4)将要插入的新元素e放入第i个位置。
(5)表长加1,插入成功,返回true。

2、顺序表的JAVA实现设计
在确定了数据的存储结构以后,按下面步骤设计顺序表.
(1) 顺序表数据元素定义--------实体类
(2) 顺序表基本操作定义--------接口
(3) 顺序表的实现类(含有一个存放数据的数组,一个指示表长度的整型变量,以及若干实现顺序表基本操作的方法)
(4) 菜单的制作

3、时间复杂度分析:
(5) 插入结点: 平均时间复杂度是O(n)。
最好情况下不需移动结点,时间复杂度是O(1)。
最坏情况下需移动表中所有结点,时间复杂度是O(n)。
(7) 删除结点: 平均时间复杂度是O(n)。
最好情况下不需移动结点,时间复杂度是O(1)。
最坏情况下需移动表中所有结点,时间复杂度是O(n)。

顺序表的优缺点:
优点:
可以随机存取表中任一元素
缺点:
在插入、删除某一元素时,需要移动大量元素,浪费存储空间,另外属于静态存储形式,数据元素的个数不能自由扩充
为克服这一缺点―――链表

2.4 Java类库中的顺序表

2.4 线性表的链式存储结构――链表
1、单链式
单链表的示意图

链式存储结构:用一组任意的存储单元存储线性表的数据元素,这组存储单元可以是连续的,也可以是不连续的。
即:结点在存储器中的位置是任意的,也就是说逻辑上相邻的数据元素在物理上不一定相邻为了能正确表示结点间的逻辑关系,在存储每个结点内容的同时,还必须存储指示其后继结点的地址next,这个地址称为指针(pointer)。

链表中的每个元素称为一个结点,每个结点由两部分组成:
数据域:存储元素内容数据
指针域:存储直接后继结点的存储位置

单链表:上述链表每个结只有一个指针域,这种链表称为单链表(Single Linked)。

单链表中设头指针head指向开始结点,由于最后一个结点无后继,故结点的指针域为空,即NULL(图示中常用^表示)。

头结点:为了处理方便,在单链表的第一个结点之前附设一个结点,称之为头结点,头结点的数据域可以不存储任何信息,也可以存储如线性表长度等类似的附加信息(想想为什么有头结点,处理方便,算法写法统一),如下图所示。
注意:头结点不记入链表的长度。
在这里插入图片描述
2、单链表的算法实现:
(0) 建立空的线性表SetNullList():产生一个空单链表,即只含一个头结点。
(1) 建立单链表SetLinkList() :产生一个单链表。
(2) 求单链表的长度Len(),返回长度整数。
(3) 取第i个元素的结点 IndexOf (i):有此节点,返回其指针,否则返回空null
(4) 查找结点Find (key):找到返回此结点的指针,找不到,返回空null
(5) 在指定位置i处插入一个结点Insert(i,node) :插入成功返回true,失败返回false
(6) 追加Append(node) :在链表尾追加一个元素,插入成功返回true,失败返回false
(7) 删除第i个结点Delete(i):若成功返回true,失败返回false
(8) 判表是否空 Empty()
(9) 遍历单链表VisitAll():遍历单链表,返回数据
注意:上述运算中,位置i的起始为1,不再象顺序表一样为0。

3、单链表的算法实现的逻辑思路:
(0) 建立空的线性表SetNullList():产生一个空单链表,即只含一个头结点。
(1) 建立单链表SetLinkList()
A)头插法建立单链表:即在头结点后不断插入新结点。
从一个空表开始,重复读入数据,生成新结点,将读入数据存放到新结点的数据域中,将该新结点插入到链表的前端,比如,读入的数据分别为abcde,则产生的结果形如:
在这里插入图片描述
B)尾插法建立单链表:即在单链表的尾部不断插入新结点。从一个空表开始,不断地将该新结点插入到链表的尾部,比如,读入的数据分别为abcde,则产生的结果形如:
在这里插入图片描述
(2) 求单链表的长度Len(),返回长度整数。
① 若头结点数据域存储单链表长度,直接返回即可。
② 若头结点数据域没有存储单链表长度,从头结点找到链表尾计算长度,下图所示。
在这里插入图片描述
(3) 取第i个元素的结点 IndexOf (i):有此节点,返回其指针,否则返回空null(说明:i为0时,取的是头结点的指针)
(说明:只能从链表的头指针出发,顺着next链指针域逐个结点往下搜索,直到搜索到第i个结点为止。因为,链表不是随机存取结构)
在这里插入图片描述
(4) 查找结点Find(key):找到返回此结点的指针,找不到,返回空null
在这里插入图片描述
(5) 在指定位置i处插入一个结点Insert(i,node) :插入成功返回true,失败返回false
(说明:插入运算是将新结点node插入到表的第i个结点的位置上,即插入到ai-1与ai之间,因此,找到ai-1结点的存储地址p是关键)
在这里插入图片描述
(6) 追加Append(node) :在链表尾追加一个元素,插入成功返回true,失败返回false
在这里插入图片描述
(7) 删除第i个结点Delete(i):若成功返回true,失败返回false
(删除运算是将表的第i个结点删去。因为在单链表中结点ai的存储地址是在其直接前趋结点ai-1的指针域next中,所以找到ai-1的存储地址p是关键)
在这里插入图片描述
(8) 判表是否空 Empty()
(9) 遍历单链表VisitAll():遍历单链表,返回数据

链表上实现插入和删除运算,无须移动结点,仅需修改指针。

4、单链表的算法的JAVA实现设计
在确定了数据的存储结构以后,按下面步骤设计单链表.
(1) 单链表数据元素定义--------实体类
(2) 单链表基本操作定义--------接口
(3) 单链表的实现类(含有一个存放数据的单链表,存放表长度成员变量,以及若干实现单链表基本操作的方法)
(4) 菜单的制作

链表包含单链表、双链表、循环链表:
单链表:结点只有一个指针域的链表,需要引入头结点来统一运算。
双链表:有两个指针域的链表。
循环链表:首尾相接的链表。

2.5 线性表的链式表示和实现

2.5、单循环链表
在单链表中,将尾部结点的指针域NULL改为指向表的头结点或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。
为了使空表和非空表的处理一致,循环链表中也可设置一个头结点。这样,空循环链表仅有一个自成循环的头结点表示。如下图所示:
在这里插入图片描述

2.5.1 单链表

1、单循环链表的优点
从任一结点开发,可以找到其他结点。
在用头指针表示的单链表中,找开始结点a1的时间是O(1),然而要找到终端结点an,则需从头指针开始遍历整个链表,其时间是O(n)
而在单循环链表中,查找任意节点的时间复杂度都是O(n)

2、单循环链表与单链表操作上的区别
单循环链表与单链表的操作基本一致,唯一的差别是,判断是否指向尾结点的条件不同。
由于单循环链表中没有NULL指针,故判断是否指向尾结点的条件,就不再像非循环单链表那样判断p或p—>next是否为空,而是判断它们是否等于头指针,而是判断p或p—>next是否等于head

2.5.2、双向(循环)链表

双向链表:在单链表的每个结点里再增加一个指向其直接前趋的指针域prior,这样形成的链表中有两个方向不同的指针链,故称为双向链表
在这里插入图片描述
在这里插入图片描述
双向链表一般由头指针H唯一确定的,将头结点和尾结点链接起来构成循环链表,并称之为双向(循环)链表。
设指针p指向某一结点,则双向链表结构存在下面的指针相等关系:
p—>prior—>next = = p = = p—>next—>prior

1、双向(循环)链表的优点
从任一结点开发,可以从两个方向找到其他结点。

2、双向(循环)链表与单循环链表操作上的区别
双向(循环)链表在多数基本运算算法的实现上,由于仅涉及一个方向的指针就可以实现,故这几个基本运算的描述与实现,与单链表及单循环链表类似,这里不再单独详述。
但是插入和删除却有很大不同,它们需要涉及两个方向的指针运算。

3、双向(循环)链表的删除第i结点运算Delete( i )
在这里插入图片描述
双向(循环)链表的删除结点i的算法步骤:
① 把指针p前进到待删除的第i结点上(调用基本运算GetNode (i))
If(p==null)
{
显示序号i错误
}
Else
{
② p–>prior–>next = p–>next;
③ p–>next–>prior = p–>prior;
}

4、双向(循环)链表在第i结点处插入新结点运算Insert(i , node)
在这里插入图片描述
双向(循环)链表插入新结点运算的算法步骤:
① 把指针p前进到待插入的第i结点位置(调用基本运算GetNode(i))
If(p==null)
{
显示序号i错误
}
Else
{
② 实例化新结点q
③ q–>next = p–>prior
④ q–>next = p–> prior–>next
⑤ q–> prior–>next = q
⑥ p–>prior = q
}

实训作业
1、线性表v的数据递增有序,试将x插入表中并保持有序性
(1)表顺序表示
(2)链表表示
(3)带有头结点的链表
2、写一个线性表的转置算法(顺序和链表表示两种情况)
(a1,a2,….,an)变为(an,an-1,….,a1)
3、有两个线性表A和B,它们的每个结点的数据部分是一个整数,请把这两个线性表A和B前后相连为一个新线性表C(A表内容在前,B表内容在后),设计并实现其基本算法,并编写菜单,实现它的各种操作。
4、编写一个程序实现两个一元多项式的相加,要求用单链表存储数据。两个一元多项式及相加后的式子如下:

  • f(x) = 3x5+4x3+2x2-8x+10
  • g(x) = x15+4x9-45x5-7x3+1
  • f(x) + g(x) = x15+4x9-42x5-3x3+2x2-8x +11
    在这里插入图片描述
    StudLinkList(单链表案例)

ILinkListOperation

package StudLinkList;

import java.io.IOException;

import StudLinkList.StudNode;

public interface ILinkListOperation {
//	(1) 建立单链表SetLinkList() :产生一个单链表。
	void SetLinkList() throws NumberFormatException, IOException;
//	(2) 求单链表的长度GetLength(),返回长度整数。
	int GetLength();
//	(3) 取第i个元素的结点 GetNode(i):有此节点,返回其指针,否则返回空null
	StudNode GetNode(int i);
//	(4) 查找结点Location(key):找到返回此结点的指针,找不到,返回空null
	StudNode Location(String key);
//	(5) 在指定位置i处插入一个结点Insert(i,node) :插入成功返回true,失败返回false
	boolean Insert(int i,StudNode node);
//	(6) 追加Append(node) :在链表尾追加一个元素,插入成功返回true,失败返回false
	boolean Append(StudNode node);
//	(7) 删除第i个结点Delete(i):若成功返回true,失败返回false
	boolean Delete(int i);
//	(8) 判表是否空 Empty()
	boolean Empty();
//	(9) 遍历单链表VisitAll():遍历单链表,返回数据
	String VisitAll();
}

RunMain

package StudLinkList;

import java.util.Scanner;

public class RunMain {

	public static void main(String[] args) {
		StudLinkList llist = new StudLinkList();
		Scanner read=new Scanner(System.in);
		String sNo = "", sName = "";
		StudNode snode = null;
		double sScore = 0;
		int i = 0;
		while (true) {
			System.out.println("********学生成绩管理程序**********");
			System.out.println("1 建立单链表");
			System.out.println("2 显示所有信息");
			System.out.println("3 求单链表的长度");
			System.out.println("4 取第i个元素的结点");
			System.out.println("5 查找结点");
			System.out.println("6 在指定位置i处插入一个结点");
			System.out.println("7 在链表尾追加一个元素");
			System.out.println("8 删除第i个结点");
			System.out.println("9 判表是否空");
			System.out.println("0 退出\n");
			System.out.println("请输入你的选择:");
			int choice =read.nextInt();
			switch (choice) {
			case 0:
				read.close();
				System.exit(0);
			case 1:
				llist.SetLinkList();
				break;
			case 2:
				String visitResult = llist.VisitAll();
				System.out.println(visitResult);
				break;
			case 3:
				int length = llist.GetLength();
				System.out.println("链表的长度为:\t" + length);
				break;
			case 4:
				System.out.println("请输入节点号i");
				i = read.nextInt();
				if (i == 0) {
					System.out.println("0号结点是头结点,内容为空串");
				} else {
					snode = llist.GetNode(i);
					if (snode == null) {
						System.out.println("未找到此结点");
					} else {
						System.out.println("此结点为:\t" + snode.studentNo + "\t"
								+ snode.name + "\t" + snode.score);
					}
				}
				break;
			case 5:
				System.out.println("请输入待查姓名");
				sName = read.next();
				snode = llist.Location(sName);
				if (snode == null) {
					System.out.println("查无此人");
				} else {
					System.out.println("此结点为:\t" + snode.studentNo + "\t"
							+ snode.name + "\t" + snode.score);
				}
				break;
			case 6:
				System.out.println("请输入学号:");
				sNo = read.next();
				System.out.println("请输入姓名:");
				sName = read.next();
				System.out.println("请输入成绩:");
				sScore =read.nextDouble();
				System.out.println("请输入插入位置:");
				i =read.nextInt();
				snode = new StudNode(sNo, sName, sScore);
				boolean iresult = llist.Insert(i, snode);
				if (iresult) {
					System.out.println("结点插入成功");
				} else {
					System.out.println("结点插入失败");
				}
				break;
			case 7:
				System.out.println("请输入学号:");
				sNo = read.next();
				System.out.println("请输入姓名:");
				sName = read.next();
				System.out.println("请输入成绩:");
				sScore = read.nextDouble();				
				snode = new StudNode(sNo, sName, sScore);
				boolean aresult = llist.Append(snode);
				if (aresult) {
					System.out.println("结点追加成功");
				} else {
					System.out.println("结点追加失败");
				}
				break;
			case 8:
				System.out.println("请输入待删节点号i");
				i = read.nextInt();
				boolean dresult=llist.Delete(i);
				if (dresult) {
					System.out.println("结点删除成功");
				} else {
					System.out.println("结点删除失败");
				}
				break;
			case 9:
				boolean eresult;
				eresult=llist.Empty();
				if(eresult)
				{
					System.out.println("当前链表为空");
				}
				else
				{
					System.out.println("当前链表不空");
				}
			}
		}
	}
}

StudLinkList

package StudLinkList;

import java.util.Scanner;

public class StudLinkList implements ILinkListOperation {

	private StudNode head = null;

	public StudLinkList() {
		head = new StudNode(); // 产生头结点
		head.next = null; // 头结点的指针域为空
	}

	/**
	 * 用头插法建立单链表
	 */
	@Override
	public void SetLinkList() {
		Scanner read=new Scanner(System.in);
		String sno = "", sname = "";
		double score = 0;
		StudNode snode = null;
		StudNode p = head;
		String todo="y";
		while(todo.toUpperCase().equals("Y")){
			System.out.println("请输入学号:");
			sno = read.next();
			System.out.println("请输入姓名:");
			sname = read.next();
			System.out.println("请输入成绩:");
			score = read.nextDouble();
			snode = new StudNode(sno, sname, score);
			snode.next = p.next;
			head.next = snode;
			System.out.println("是否继续输入结点信息(y/n)?");
			todo=read.next();
		} 	
	}

	/**
	 * 获取线性链表的长度
	 */
	@Override
	public int GetLength() {
		StudNode p = null;
		p = head.next;
		int s = 0;
		while (p != null) {
			s++;
			p = p.next;
		}
		return s;
	}

	/**
	 * 取第i个元素的结点:有此节点,返回其指针,否则返回空null,当i为0时,取的是头结点的指针
	 */
	@Override
	public StudNode GetNode(int i) {
		StudNode p;
		p = head;
		if (i < 0) {
			return null;
		} else {
			int j = 0;
			while ((j != i) && (p != null)) {
				p = p.next;
				j++;
			}
			return p;
		}
	}

	/**
	 * 按关键字查找结点:找到返回此结点的指针,找不到,返回空null
	 */
	@Override
	public StudNode Location(String key) {
		StudNode p;
		p = head.next;
		while (p != null && !(p.name.equals(key))) {
			p = p.next;
		}
		return p;
	}

	/**
	 * 在指定位置i处插入一个结点:插入成功返回true,失败返回false
	 */
	@Override
	public boolean Insert(int i, StudNode node) {
		StudNode p;
		p = GetNode(i - 1);
		if (p == null) {
			System.out.println("插入位置不正确!");
			return false;
		} else {
			node.next = p.next;
			p.next = node;
			return true;
		}
	}

	/**
	 * 向链表尾部追加结点,插入成功返回true,失败返回false
	 */
	@Override
	public boolean Append(StudNode node) {
		StudNode p;
		p = head;
		while (p.next != null) {
			p = p.next;
		}
		node.next = null;
		p.next = node;
		return true;
	}

	/**
	 * 删除第i个结点:若成功返回true,失败返回false
	 */
	@Override
	public boolean Delete(int i) {
		// TODO Auto-generated method stub
		StudNode p;
		p = GetNode(i - 1);
		if (p == null || p.next == null) {
			System.out.println("待删序号i不正确");
			return false;
		} else {
			p.next = p.next.next;
			return true;
		}
	}

	/**
	 * 判表是否空
	 */
	@Override
	public boolean Empty() {
		return head.next == null ? true : false;
	}

	@Override
	public String VisitAll() {
		StudNode p = null;
		StringBuilder sb = new StringBuilder();
		p = head.next;
		while (p != null) {
			sb.append(p.studentNo + "\t" + p.name + "\t" + p.score + "\n");
			p = p.next;
		}
		return sb.toString();
	}

}

StudNode

package StudLinkList;

public class StudNode {
	String studentNo;// 学号
	String name; // 姓名
	double score; // 分数
	StudNode next=null; //指针(引用)字段
	public StudNode(){		
	}
	public StudNode(String studentNo, String name, double score) {
		this.studentNo = studentNo;
		this.name = name;
		this.score = score;
	}
	
	public String getStudentNo() {
		return studentNo;
	}

	public void setStudentNo(String studentNo) {
		this.studentNo = studentNo;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}
}

总的源码在这里,免费哦!(直接跳转)
么么叽么么叽么么哒!
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

莫白媛

老板给个铜板啊!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值