自己的理解解
分享给大家。线性表,顺序表,和链表
之间的区别和联系!逻辑结构
, 就是对外暴露数据之间的关系,不关心底层如何实现。物理结构
,他是实现一个结构实际物理地址上的结构。比如顺序表就是用数组
实现。而链表用指针
完成主要工作。不同的结构在不同的场景有不同的区别。List
接口类型,这就是逻辑结构,因为他就是封装了一个线性关系的一系列方法和数据。而具体的实现其实就是跟物理结构相关的内容。比如顺序表的内容存储使用数组的,然后一个get,set,add方法都要基于数组
来完成,而链表是基于指针
的。当我们考虑对象中的数据关系就要考虑指针
的属性。指针的指向和value。下面用一个图来浅析线性表的关系。可能有些不太确切,但是其中可以参考,并且后面也会根据这个图举例。
线性表
来说。不管
它的具体实现
方法如何,我们应该有的函数名称
和实现效果
应该一致。你也可以感觉的到在一些结构的设计。比如List的Arraylist
和LinkedList
。Map的HashMap和currentHashMap他们的接口api都是相同的,但是底层设计实现肯定是有区别的。继承
这个接口的方法,提高程序的可读性。固定类型
(int),随着知识的进步,我们应当采用泛型
来实现更合理。至于接口的具体设计如下:package LinerList;
public interface ListInterface<T> {
void Init(int initsize);//初始化表
int length();
boolean isEmpty();//是否为空
int ElemIndex(T t);//找到编号
T getElem(int index) throws Exception;//根据index获取数据
void add(int index,T t) throws Exception;//根据index插入数据
void delete(int index) throws Exception;
void add(T t) throws Exception;//尾部插入
void set(int index,T t) throws Exception;
String toString();//转成String输出
}
数组data
和一个length
.固定大小
,但是笔者为了可用性如果内存不够将扩大二倍
。当然这样很可能因为空间使用问题造成很大的浪费
。add(int index,T t)
性能表现最差
的地方,频繁的插入,删除。空一个小板凳
后面的人需要往前挪
。
getElem(int index)
,你可以直接根据数据坐标返回。a[index],而其他操作,可以通过遍历直接操作数组
即可。指针
。很多人说java
没指针,其实java他也有隐形指针。只不过不能直接用罢了。另一个同等级对象
。对于这个关系,你可以比作每个person类。每个person类都有老公(老婆)
,而这个老公老婆也是一个实际对象,可以理解这更像一种逻辑约定关系
,而不是硬生生的关系吧。脑子记忆
。上面的顺序表我们说它有序因为每个小板凳(数组)有编号
,我们可以根据这个来确定位置。而对于链表来说,你可以看作成一个站在操场上的一队人。而他的操作也略有不同,下面针对一些比较特殊和重要的进行归纳。对于线性表,我们只需要一个data数组和length就能表示基本信息。而对于链表,我们需要一个node(head头节点)
,和length
,当然,这个node也是一个结构体。
class node<T>{
T data;//节点的结果
node next;//下一个连接的节点
public node(){}
public node(T data)
{
this.data=data;
}
public node(T data, node next) {
this.data = data;
this.next = next;
}
}
当然,这个节点有数据域
和指针域
。数据域就是存放真实的数据,而指针域就是存放下一个node的指针。所以相比顺序表,如果用满数组情况下,链表占用更多的资源,因为它要存放指针占用资源。
add(int index,T t)
其中index为插入的编号位置,t为插入的数据
加入插入一个节点node
,根据index找到插入的前一个节点叫pre
。那么操作流程为
node.next=pre.next
如下1的操作,将插入节点后面联系起来。此时node.next和pre.next一致。pre.next=node
因为我们要插入node,而node链可以替代pre自身的next。那么直接将pre指向node。那么就相当于原始链表插入了一个node。
很多人搞不清什么是带头节点
和不带头节点
。带头节点就是head节点不放数据,第0项从head后面那个开始数。而不带头节点的链表head放数据,head节点就是第0位
。
主要区别:
可看作一列火车的火车头
)。而方便的就是带头节点在首位插入更简单。因为插入第0位
也是在head的后面
。next
为null
。不能和插入普通位置相比!按照index移除:delete(int index)
按照尾部移除(拓展):deleteEnd()
node.next!=null
时node=node.next
指向下一个node.next==null
时候。说明这个节点时最后一个。你可以node=null
。这个这个node的前驱pre的next就是null。这个节点就被删除了。头部删除(带头节点):
head.next(第1个元素)
=head.next.next(第二个元素)
头部删除(不带头节点)
第一个
就是存货真价实的元素
的。不带头节点删除也很简单。直接将head移到第二位
就行了。即:head=head.next
从head开始
。然后另一个节点team=head
或head.next
。然后用这个节点不停的等于它指向的next去查找我们需要的内容即while(循环条件){team=team.next}类似。if else
.package LinerList;
public class seqlist<T> implements ListInterface<T> {
private Object[] date;//数组存放数据
private int lenth;
public seqlist() {//初始大小默认为10
Init(10);
}
public void Init(int initsize) {//初始化
this.date=new Object[initsize];
lenth=0;
}
public int length() {
return this.lenth;
}
public boolean isEmpty() {//是否为空
if(this.lenth==0)
return true;
return false;
}
/*
* * @param t
* 返回相等结果,为-1为false
*/
public int ElemIndex(T t) {
// TODO Auto-generated method stub
for(int i=0;i<date.length;i++)
{
if(date[i].equals(t))
{
return i;
}
}
return -1;
}
/*
*获得第几个元素
*/
public T getElem(int index) throws Exception {
// TODO Auto-generated method stub
if(index<0||index>lenth-1)
throw new Exception("数值越界");
return (T) date[index];
}
public void add(T t) throws Exception {//尾部插入
add(lenth,t);
}
/*
*根据编号插入
*/
public void add(int index, T t) throws Exception {
if(index<0||index>lenth)
throw new Exception("数值越界");
if (lenth==date.length)//扩容
{
Object newdate[]= new Object[lenth*2];
for(int i=0;i<lenth;i++)
{
newdate[i]=date[i];
}
date=newdate;
}
for(int i=lenth-1;i>=index;i--)//后面元素后移动
{
date[i+1]=date[i];
}
date[index]=t;//插入元素
lenth++;//顺序表长度+1
}
public void delete(int index) throws Exception {
if(index<0||index>lenth-1)
throw new Exception("数值越界");
for(int i=index;i<lenth;i++)//index之后元素前移动
{
date[i]=date[i+1];
}
lenth--;//长度-1
}
@Override
public void set(int index, T t) throws Exception {
if(index<0||index>lenth-1)
throw new Exception("数值越界");
date[index]=t;
}
public String toString() {
String vaString="";
for(int i=0;i<lenth;i++)
{
vaString+=date[i].toString()+" ";
}
return vaString;
}
}
package LinerList;
class node<T>{
T data;//节点的结果
node next;//下一个连接的节点
public node(){}
public node(T data)
{
this.data=data;
}
public node(T data, node next) {
this.data = data;
this.next = next;
}
}
public class Linkedlist<T> implements ListInterface<T>{
node head;
private int length;
public Linkedlist() {
head=new node();
length=0;
}
public void Init(int initsize) {
head.next=null;
}
public int length() {
return this.length;
}
public boolean isEmpty() {
if(length==0)return true;
else return false;
}
/*
* 获取元素编号
*/
public int ElemIndex(T t) {
node team=head.next;
int index=0;
while(team.next!=null)
{
if(team.data.equals(t))
{
return index;
}
index++;
team=team.next;
}
return -1;//如果找不到
}
@Override
public T getElem(int index) throws Exception {
node team=head.next;
if(index<0||index>length-1)
{
throw new Exception("数值越界");
}
for(int i=0;i<index;i++)
{
team=team.next;
}
return (T) team.data;
}
public void add(T t) throws Exception {
add(length,t);
}
//带头节点的插入,第一个和最后一个一样操作
public void add(int index, T value) throws Exception {
if(index<0||index>length)
{
throw new Exception("数值越界");
}
node<T> team=head;//team 找到当前位置node
for(int i=0;i<index;i++)
{
team=team.next;
}
node<T>node =new node(value);//新建一个node
node.next=team.next;//指向index前位置的下一个指针
team.next=node;//自己变成index位置
length++;
}
@Override
public void delete(int index) throws Exception {
if(index<0||index>length-1)
{
throw new Exception("数值越界");
}
node<T> team=head;//team 找到当前位置node
for(int i=0;i<index;i++)//标记team 前一个节点
{
team=team.next;
}
//team.next节点就是我们要删除的节点
team.next=team.next.next;
length--;
}
@Override
public void set(int index, T t) throws Exception {
// TODO Auto-generated method stub
if(index<0||index>length-1)
{
throw new Exception("数值越界");
}
node<T> team=head;//team 找到当前位置node
for(int i=0;i<index;i++)
{
team=team.next;
}
team.data=t;//将数值赋值,其他不变
}
public String toString() {
String va="";
node team=head.next;
while(team!=null)
{
va+=team.data+" ";
team=team.next;
}
return va;
}
}
package LinerList;
public class test {
public static void main(String[] args) throws Exception {
// TODO Auto-generated method stub
System.out.println("线性表测试:");
ListInterface<Integer>list=new seqlist<Integer>();
list.add(5);
list.add(6);
list.add(1,8);
list.add(3,996);
list.add(7);
System.out.println(list.ElemIndex(8));
System.out.println(list.toString());
list.set(2, 222);
System.out.println(list.toString());
list.delete(4);
System.out.println(list.toString());
System.out.println(list.length());
System.out.println("链表测试:");
list=new Linkedlist<Integer>();
list.add(5);
list.add(6);
list.add(1,8);
list.add(3,996);
list.add(7);
System.out.println(list.ElemIndex(8));
System.out.println(list.toString());
list.set(2, 222);
System.out.println(list.toString());
list.delete(4);
System.out.println(list.toString());
System.out.println(list.length());
}
}
输出:
线性表测试: 1 5 8 6 996 7 5 8 222 996 7 5 8 222 996 4 链表测试: 1 5 8 6 996 7 5 222 6 996 7 5 222 6 996 4
查询速度较慢
,因为他需要从头遍历。如果频繁操作尾部,可以考虑链表中不仅有head在加尾tail
节点。而顺序表查询速度虽然快但是插入很费时费力。实际应用根据需求选择!不用造轮子
,可以直接用,但是还是很有学习价值
的。