线性表的存储结构分为 顺序结构存储 和 链式结构存储
线性表是最常用,最简单的一种数据结构,简而言之,线性表是N个数据元素的有限序列。每个数据元素最多有一个前驱元素,每个元素最多只有一个直接后继元素;只有第一个数据元素没有直接前驱元素,而最后一个元素没有后继元素
在实现线性表的不同存储形式之前,先顶一个接口用于表述线性表的所有操作
package com.sshhsun.list;
public interface LList<T> {
boolean isEmpty();//判断是否为空
int length(); //得到长度
T get(int i); //返回第i个元素
void set(int i,T x); //设置第i个元素值x
void insert(int i,T x); //插入第i个元素值为x,如果超过数组长度则新建一个长度为原来2倍的集合并将原来的值付给新表
void append(T x); //在线性表最后插入x元素
T remove(int i);//删除并返回第i个元素
void removeAll();//清空表
int search(T key);//返回首次出现的关键字为key的元素位置
}
顺序表
线性表的实现结构MSeqList类实现LList接口的功能
package com.sshhsun.list;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MSeqList<T> implements LList<T> {
private Object[] element;
private int len;
public MSeqList(int size) {
this.element = new Object[size];
this.len = 0;
}
public MSeqList() {// 默认一个长度
this(64);
}
@Override //判断是否为空
public boolean isEmpty() {
return this.len == 0;
}
@Override//得到长度
public int length() {
return this.len;
}
@Override //返回第i个元素
public T get(int i) {
if (i >= 0 && i < this.len) {
return (T) this.element[i];
}
return null;
}
@Override//设置第i个元素值x
public void set(int i, T x) {
if (x == null) {
return;
}
if (i >= 0 && i < this.len) {
this.element[i] = x;
} else {
throw new IndexOutOfBoundsException(i + "");
}
}
@Override//插入第i个元素值为x
public void insert(int i, T x) {
if (x == null) {
return;
}
if (this.len == this.element.length) {
Object[] temp = this.element;
this.element = new Object[temp.length * 2];
for (int j = 0; j < temp.length; j++) {
this.element[j] = temp[j];
}
}
if (i < 0 || i > this.len) {
throw new IndexOutOfBoundsException(i + "");
}
for (int j = this.len - 1; j >= i; j--) {
this.element[j + 1] = this.element[j];
}
this.element[i] = x;
this.len++;
}
@Override
public String toString() {
StringBuilder str = new StringBuilder("(");
if (this.len > 0) {
str.append(this.element[0].toString());
}
for (int i = 1; i < this.len; i++) {
str.append("," + this.element[i].toString());
}
str.append(")");
return str.toString();
}
@Override//在线性表最后插入x元素
public void append(T x) {
insert(this.len, x);
}
@Override//删除并返回第i个元素
public T remove(int i) {
if (this.len == 0 || i < 0 || i > this.len) {
return null;
}
T old = (T) this.element[i];
for (int j = i; j < this.len - 1; j++) {
this.element[j] = this.element[j + 1];
}
this.len--;
return old;
}
@Override//清空表
public void removeAll() {
for (int i = 0; i < this.len; i++) {
this.element[i] = null;
}
this.len = 0;
}
@Override//返回首次出现的关键字为key的元素位置
public int search(T key) {
if (key == null || this.len == 0) {
return -1;
}
for (int i = 0; i < this.len; i++) {
if (this.element[i] == key) {
return i;
}
}
return -1;
}
}
顺序表的操作效率分析
1.顺序表存取任何一个元素的get(),set()方法时间复杂度是 O(1).;
2.对顺序表的插入和删除操作,算法所花费的时间主要用于移动元素。在等概率的情况下,插入一个元素平均需要移动一半的元素,时间复杂度为O(n),删除一个元素的时间复杂度也是O(n).
顺序表解决一个很经典的问题,约瑟夫环的问题
使用顺序表MSeqList解决约瑟夫环的问题:创建一个具有n个元素的顺序表对象List;从第s个元素开始,依次计数,每数到d,就删除对应元素,重复计数并删除元素,直到剩下一个元素。
上代码 !
package com.sshhsun.list;
public class Josephus {
public Josephus(int number, int start, int distance) {
MSeqList<String> list = new MSeqList<String>();
for (int i = 0; i < number; i++) {
list.append((char) ('A' + i) + "");
}
System.out.println("约瑟夫环(" + number + "," + start + "," + distance
+ ")");
System.out.println(list.toString());
int i = start;
while (list.length() > 1) {
i = (i + distance - 1) % list.length(); // 计数按循环规律变化,顺序表可看作是环形结构
System.out.print("删除" + list.remove(i).toString() + " ,");
System.out.println(list.toString());
}
System.out.println("最后的人是" + list.get(0).toString());
}
public static void main(String[] args) {
new Josephus(5, 0, 3);
}
}
结果: