学习总结
线性表是最基本、最简单、最常田的线性结构、几平所有的程序设计语言中都包含数组这种数据结构,而一个数组中的若干数据元素是按照顺序存储在相邻的计算机内存中,这就是一种典型的线性表。
2.1.1 线性表的定义
线性表(Linear List)是由n(n≥0)个类型相同的数据兀系ao, a,…,an-1 组成的有限序列,记作(a, ap,…,an-1),其中数据元素a可以是整型、实型、字符、布尔或复合类型等任何一种数据类型;n是线性表数据元素的个数,即线性表的长度。当n=0时,称为空表;当n>0时,a;(0<i<n-1)有且仅有一个直接前驱a_,和-直接后继a;1,a。没有前驱元素称为起始结点,a,没有后继元素称为终端结点
2.1.2 线性表抽象数据类型描述及定义
线性表的抽象数据类型主要包括两个方面:数据集合和该数据集合上的操作集合。线性表的常见操作有插入、删除、查找、获取元素值、设置元素值等,在 Java语言中,抽象数据类型通常设计成接口。线性表抽象数据类型的Java接口可声明如下:
//线性表抽象数据类型,T表示数据元素的数据类型
public interface LinearList<T>{
public T get (int i); //返回下标为i的元素
public void set(int i,T t); //用指定元素t替换列表中指定位置i的元素
public int insert (T t); //在线性表的最后插入元素t,返回t序号
public int insert (int i. Tt); //在线性表的位置i处插入元素t,返回t序号
public T remove(int i) ; //删除下标为i的元素,返回被删除的元素
public boolean contains (T kev); //判断线性表中是否包含key元素
public int indexof (T key);
//在线性表中查找首次出现的与key相等的元素,返回元素位置若不存在,返回-1
public int size (); //返回线性表的长度
public void clear(); //清空线性表
public boolean isEmpty(); //判断线性表是否为空
public void printList(); //遍历线性表所有元素
}
【注意】上述声明中,i表示数据元素的下标,约定0≤i≤n-1,n为线性表长度;T为泛型,表示线性表中数据元素的数据类型。Java语言约定,T的实际参数必须是类,不能是int、 double、char 等基本数据类型;若需表示基本数据类型,则须采用基本数据类型包装类,如 Integer、Double、Character等。
其实,Java语言中 java.util 包中的List接口就是线性表,它的实现子类ArrayList就是顺序表,LinkedList就是链表。
2.2.1 顺序表1.顺序表的定义
使用一组地址连续的存储单元依次存储线性表的各个数据元素,该结构称作线性表的顺序存储结构,简称为顺序表(Sequential List)。
在程序设计语言中,通常使用数组来表示线性表的顺序存储结构。
2.2.2顺序表的基本操作
1.顺序表类声明、构造方法、存取操作实现
声明顺序表类SeqList,实现线性表接口LinearList,顺序表类包含2个保护权限成员变量data和n。其中,data数组存放顺序表中的数据元素,元素类型用泛型T表示;n为顺序表中的元素个数(顺序表长度)。
//顺序表类,T表示数据元素的数据类型
public class SeqList<T> implements LinearList<T>{
protected T[]data; //对象数组存储顺序表的数据元素
protected int n; //顺序表数据元素个数(长度)
public SeqList(int length) { //构造容量为length的空表
this.data=new T[length]; //创建长度为length的数组,元素均为null
this.n=0; //空表长度为0
}
public SeqList(T[] values){ //由values 数组构造顺序表
this(values. length); //创建容量为values.length 的空表
for (int i=0; i<values.length; i++){ //复制数组元素
this.data[i]=values[il;//对象引用赋值
}
this.n=values.length; //顺序表长度为数组values的长度
}
public SeqList(){ //创建默认容量为10的空表
this(10);
} //构造方法重载,调用本类已声明的指定参数列表的构造方法
@override
public T get (int i) { //返回下标为i的数据元素a1
if (i>=0 && i<this.n) {
return this.data[i];
}else{
throw new IndexoutOfBoundsException(i+”下标越界");
}
//抛出下标越界异常
}
@override
public void set (int i,T t){ //用指定元素t替换列表中的元素ai
if(t--null)
throw new NullPointerException ("t==null");
//抛出空对象异常
if(i>=0&& i<this.n){
this.data[i]-t;
}else {
throw new IndexoutofBoundsException(i+"下标越界");
//抛出下标越界异常
}
}
//其他方法的具体实现代码将在后面介绍
}
2.求表长、清空、判断表空
这3个算法比较简单,时间复杂度均为O(1),具体实现代码如下:
(1)求顺序表的长度
直接返回顺序表的长度值n即可
public int size(){
return this.n;//返回顺序表的长度
}
(2)清空顺序表
只要将顺序表的长度置为0即可
public void clear() {
this.n=0;
}
(3)判断顺序表是否为空
只要判断顺序表的长度是否为0即可。
public boolean isEmpty() {
return this.n==0;//判断顺序表是否为空
}
3.遍历顺序表
从下标О到下标n-1,循环输出顺序表中的每一个数据元素,时间复杂度为O(n),
具体实现代码如下:
public void printList(){ //遍历顺序表所有元素
for (int i=0; i<this.n; i++) {
system.out.println (this.data[i]);
}
}
4.顺序表的查找
在顺序表中查找数据元素key,需要将key与顺序表元素逐个进行比较是否相等。若找到,返回首次出现的位置;若不存在,则返回-1。
比较对象是否相等的规则由数据元素所属T类的equals(Object)方法实现。
(1)查找算法
//在顺序表查找首次出现的与kev相等元素,返回元素位置,若不存在返回-1
public int indexOf(T key){
for (int i=0; i<this.n; i++){
if(key.equals (this.data[i])){
return i;
}
}
return -1;
}
(2)判断是否包含某元素算法
public boolean contains(T key){ //判断顺序表中是否包含key元素
for (int i=0; i<this.n; i++){
if (key.equals (this.data[i])){
return true;
}
}
return false;
}
5.顺序表的插入
public int insert(int i, T t){ //在顺序表的位置主处插入元素t,返回t序号
if(t==null)
throw new NullPointerException("t=-null"); //抛出空对象异常
if(i>=0 && i<=this.n) {
Tsrc[]=this.data; //保存原数组
if (this.n==data.length){ //若数组满,则扩充顺序表的数组容量
this.data=new T[src.length*2];
//重新申请一个容量为原来2倍的数组
for(int j=0;j<i; j++){
this.data[j]=src[j]; //复制当前数组前i个元素
}
}
for (int j-this.n-l; j>=i; j--){
//从最后一个位置开始到位置i处,每一个元素向后移动1位
this.data[j+1]=src[j];
}
this.data[i]=t; //把要插入的元素t存放在位置i处
this.n++;
return i; //返回下标
}else{
throw new IndexoutOfBoundsException(i+”下标越界"); //抛出下标越界异常
}
}
public int insert(T t){
return this.insert(this.n.t);
}
6.顺序表删除
public T remove (int i){
if( this.n==O){
try{
throw new Exception("空表无法删除!");
}catch (Exception e) {
e.printstackTrace(;
}
}
if(i>-0 && i<this.n)(
Told-this.data[i]; //old中存放被删除元素
for (int j-i; j<this.n-1;j++){
this.data[j]=this.data[j+1]; //从位置i+1 开始每个元素依次前移一位
}
this.data[ this.n-1]=null;//设置最后一个位置元素为空
this.n--;
return old;
}else {//抛出下标越界异常
throw new IndexoutOfBoundsException(i+"下标越界");
}
}
2.3 线性表的链式存储结构——链表
线性表的链式存储结构称为线性链表,简称链表。常见的链表有单链表、循环链表和双向链表。链表的优点是数据的插入或删除都非常方便,不需要移动大量数据;缺点是设计数据结构时稍显麻烦,并且在查找数据时,无法像顺序表那样可随机读取,必须按顺序找到该数据为止。
链表是用一组任意的存储单元(可以是连续的,也可以是不连续的存储单元)来存储线性表中的数据元素,逻辑上相邻的数据元素在物理位置上不一定相邻,因此,必须采用附加信息表示数据元素之间的逻辑关系。存储一个数据元素的存储单元称为结点(Node),一个结点至少包含两个部分:
结点(数据域,地址域)
其中,数据域存储数据元素本身,地址域(也称为引用域、指针域、链)存储前驱或后继结点的地址。地址域确定了数据元素之间的逻辑关系。
2.3.1单链表的结构
1.单链表的定义
每个结点只有一个地址域的线性链表称为单链表(Singly Linked List),结点结构如下:
单链表结点(数据域data,后继结点地址域next)
单链表有带头结点和不带头结点两种。图2-4所示为带头结点的单链表,指向单链表的指针 head称为头指针,头指针所指向的不存放数据元素的第一个结点称为头结点,存放第一个数据元素a。的结点称为开始结点,存放最后一个数据元素a_的结点称为尾结点。
1. 1单链表结点类
public class Students {
String name;
int id;
int score;
public Students next;
public Students(String name, int id, int score) {
this.name = name;
this.id = id;
this.score = score;
}
public String getName() {
return name;
}
public int getNumber() {
return id;
}
public int getScore() {
return score;
}
public Students getNext() {
return next;
}
public void setName(String name) {
this.name = name;
}
public void setNumber(int number) {
this.id = number;
}
public void setScore(int score) {
this.score = score;
}
public void setNext(Students next) {
this.next = next;
}
@Override
public String toString() {
return "Students{" +
"name='" + name + '\'' +
", number=" + id +
", score=" + score ;
}
}
1.2方法实现
public class StudentsList {
Students head = new Students("", 0, 0);
public Students getHead() {
return head;
}
//添加,在链表的尾部添加元素
public void add(Students s) {
Students temp = head;
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
}
//Students newnode = new Students(s.name,s.id,s.score);
temp.next = s;
}
//遍历
public void list() {
if (head.next == null) {
System.out.println("链表为空");
}
Students temp = head;
while (true) {
if (temp.next == null) {
break;
}
temp = temp.next;
System.out.println(temp);
}
}
//删除
public void remove(int i) {
if (head.next == null) {
System.out.println("链表为空");
return;
}
Students temp = head;
boolean flag = false;
while (true) {
if (temp.next == null) {
break;
}
if (temp.next.id == i) {
flag = true;
break;
}
temp = temp.next;
}
if (flag) {
temp.next = temp.next.next;
} else {
System.out.println("元素不存在");
}
}
}
1.3实现代码
public class Demo {
public static void main(String[] args) {
Students s1 = new Students("meiko",1,80);
Students s2 = new Students("marry",2,90);
Students s3 = new Students("jack",3,92);
Students s4 = new Students("aa",4,80);
Students s5 = new Students("bb",5,99);
Students s6 = new Students("cc",6,100);
//表1
StudentsList list1 = new StudentsList();
list1.add(s1);
list1.add(s2);
list1.add(s3);
System.out.println("表1:");
list1.list();
//表2
StudentsList list2 = new StudentsList();
list2.add(s4);
list2.add(s5);
list2.add(s6);
System.out.println("表2:");
list2.list();
//删除
System.out.println("删除表1学号为2的学生");
list1.remove(2);
list1.list();
//合并
System.out.println("合并");
StudentsList list3 = new StudentsList();
list3.merge(list1,list2,list3);
list3.list();
}
4.一些方法其它算法
(1)求表长、清空、判断表空
public int size(){
int n = 0;
Students temp = head.next;
While(temp!null){
temp++;
temp=temp.next;
}
return n;
}
(2) 清空单链表
public void clear(){
head.next = null;
//清空单链表
}
太多了太多了 不一一总结了 总结主要的就好了