2.1 线性表的逻辑概念2.2 线性表的基本运算(或基本 *** 作):2.3 线性表的顺序表示和实现2.4 Java类库中的顺序表2.5 线性表的链式表示和实现2.5.1 单链表2.5.2、双向(循环)链表
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 线性表的基本运算(或基本 *** 作):
(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 线性表的顺序存储结构――顺序表
指用一组地址连续的存储单元,依次存储线性表中的数据元素,具体的说,就是用数组来存储顺序表。
其特征是,逻辑上相邻的元素结点,物理存储位置上也相邻。
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 线性表的链式存储结构――链表
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、单循环链表
在单链表中,将尾部结点的指针域NULL改为指向表的头结点或开始结点,就得到了单链形式的循环链表,并简单称为单循环链表。
为了使空表和非空表的处理一致,循环链表中也可设置一个头结点。这样,空循环链表仅有一个自成循环的头结点表示。如下图所示:
1、单循环链表的优点
从任一结点开发,可以找到其他结点。
在用头指针表示的单链表中,找开始结点a1的时间是O(1),然而要找到终端结点an,则需从头指针开始遍历整个链表,其时间是O(n)
而在单循环链表中,查找任意节点的时间复杂度都是O(n)
2、单循环链表与单链表 *** 作上的区别
单循环链表与单链表的 *** 作基本一致,唯一的差别是,判断是否指向尾结点的条件不同。
由于单循环链表中没有NULL指针,故判断是否指向尾结点的条件,就不再像非循环单链表那样判断p或p—>next是否为空,而是判断它们是否等于头指针,而是判断p或p—>next是否等于head
双向链表:在单链表的每个结点里再增加一个指向其直接前趋的指针域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+10g(x) = x15+4x9-45x5-7x3+1f(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; } @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; } } @Override public StudNode Location(String key) { StudNode p; p = head.next; while (p != null && !(p.name.equals(key))) { p = p.next; } return p; } @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; } } @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; } @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; } }
总的源码在这里,免费哦!(直接跳转)
么么叽么么叽么么哒!
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)