线性表
线性表是最简单、最基本、最常用的数据结构。线性表是线性结构的抽象(Abstract),线性结构的特点是结构中的数据元素之间存在一对一的线性关系。这种一对一的关系指的是数据元素之间的位置关系,即:(1)除第一个位置的数据元素外,其它数据元素位置的前面都只有一个数据元素;(2)除最后一个位置的数据元素外,其它数据元素位置的后面都只有一个元素。也就是说,数据元素是一个接一个的排列。因此,可以把线性表想象为一种数据元素序列的数据结构。
线性表就是位置有先后关系,一个接着一个排列的数据结构。
CLR中线性表
- c#1.1提供了一个非泛型接口IList接口,接口中的项是Object,实现IList接口的类有 :ArrayList,ListDictionary,StringCollection,stringDictionary。
- c# 2.0 提供了泛型的IList接口,实现了List接口的类有List。
C# List的基础常用方法:
一、声明:
1、List<T> mList = new List<T>();
//T为列表中元素类型,现在以string类型作为例子:
List<string> mList = new List<string>();
//以一个集合作为参数创建List:
2、List<T> testList =new List<T>(IEnumerable<T> collection);
string[] temArr = { "Ha", "Hunter", "Tom", "Lily", "Jay", "Jim", "Kuku", "Locu"};
List<string> testList = new List<string>(temArr);
二、添加元素:
1、List.Add(T item)//添加一个元素
例:mList.Add("John");
2、List.AddRange(IEnumerable<T> collection)//添加一组元素
例:string[] temArr = {"Ha","Hunter","Tom","Lily","Jay","Jim","Kuku","Locu"};
mList.AddRange(temArr);
3、Insert(index, T item);//在index位置添加一个元素
例:mList.Insert(1,"Hei");
三、遍历List中元素:
foreach(T element in mList)//T的类型与mList声明时一样
{
Console.WriteLine(element);
}
例:
foreach(s trings in mList)
{
Console.WriteLine(s);
}
四、删除元素:
1、List. Remove(T item)删除一个值
例:mList.Remove("Hunter");
2、List. RemoveAt(int index);删除下标为index的元素
例:mList.RemoveAt(0);
3、List. RemoveRange(int index,int count);
从下标index开始,删除count个元素
例:mList.RemoveRange(3, 2);
五、判断某个元素是否在该List中:
List. Contains(T item)返回true或false,很实用
例:if(mList.Contains("Hunter"))
{
Console.WriteLine("There is Hunter in the list");
}
else
{
mList.Add("Hunter");
Console.WriteLine("Add Hunter successfully.");
}
六、给List里面元素排序:
List.Sort ()//默认是元素第一个字母按升序
例:mList.Sort();
七、给List里面元素顺序反转:
List. Reverse ()//可以不List.Sort ()配合使用,达到想要的效果
例:mList.Sort();
八、List清空:
List. Clear ()
例:mList.Clear();
九、获得List中元素数目:
List.Count ()//返回int值
例:in tcount = mList.Count();
Console.WriteLine("The num of elements in the list: "+count);
线性表接口的定义
public Program
{
static void Main(string[] args)
{
//使用我们自己的顺序表
SeqList<string> seqList = new SeqList<string> ();
seqList.Add("123");
seqList.Add("456");
seqList.Add("789");
Console.WirteLine(seqList.GetEle(0));
Console.WirteLine(seqList[0]);
seqList.Insert("777");
for(int i = 0 ; i<seqList.GetLength();i++)
{
Console.WirteLine(seqList[i]+ " ");
}
}
}
//线性表的定义
public interface IListDS<T>
{
int GetLength();//求长度
void Clear();//清空操作
bool IsEmpty();//判断线性表是否为空
void Append(T item);//附加操作、添加元素 相当于Add
void Insert(T item , int i );//插入操作
T Delete(int i);//删除操作
T GetElem(int i);//取表元
T this[int index]{get;};//定义一个索引器获取元素
int Locate(T value);//按值查找
}
//线性表的实现
//顺序表实现方式
class SeqList<T> : IListDS<T>
{
private T[] data;//用来存储数据
private int count = 0;//表示存了多少个数据
//构造方法用来初始化数据
public SeqList(int size)//size为最大容量
{
data = new T[size];
count = 0;
}
//默认的构造函数
public SeqList():this(10)//默认构造函数容量是 10
{
}
//取得数据的个数
public int GetLength()
{
return count;
}
//清空数据
public void Clear()
{
count = 0;
}
//判断数据是否为空
public bool IsEmpty()
{
return count == 0 ;
}
//数据的添加
public void Add(T item)
{
if(count == data.Length)
{
Console.WirteLine("当前顺序表已存满,不允许在存入")
}
else
{
data[count] = item;
count++;
}
}
//插入
//顺序表的插入是指在顺序表的第i个位置插入一个值为item的新元素,
//插入后使 原 表 长 为 n 的 表 (a1,a2, … ,ai-1,ai,ai+1, … ,an) 成 为 表
//长 为 n+1 的 表(a1,a2,…,ai-1,item,ai,ai+1,…,an)。
//i的取值范围为 1≤i≤n+1,i为n+1 时,表示在顺序表的末尾插入数据元素。
//顺序表上插入一个数据元素的步骤如下:
//(1)判断顺序表是否已满和插入的位置是否正确,表满或插入的位置不正确不能插入;
//(2)如果表未满和插入的位置正确,则将an~ai依次向后移动,为新的数据元素空出位置。在算法中用循环来实现;
//(3)将新的数据元素插入到空出的第 i 个位置上;
//(4)修改 last(相当于修改表长) ,使它仍指向顺序表的最后一个数据元素。
public void Insert(T item , int i )
{
for(int j == count-1;j >= i ; j--)
{
data[i+1] = data[i];
}
data[i] = item ;
count ++ ;
}
//算法的时间复杂度分析:顺序表上的插入操作,时间主要消耗在数据的移动上,
//在第i个位置插入一个元素, 从ai到an都要向后移动一个位置, 共需要移动n-i+1
//个元素,而i的取值范围为 1≤i≤n+1,当i等于 1 时,需要移动的元素个数最多,为n个;
//当i为n+1 时,不需要移动元素。设在第i个位置做插入的概率为pi,则平均移动数据
//元素的次数为n/2。这说明:在顺序表上做插入操作平均需要移动表中一半的数据元素,
//所以,插入操作的时间复杂度为O(n) 。
//元素的删除
public T Delete(int i)
{
T temp = data[index];
for(int i = index+1 ; i < count ; i ++)
{
data[i-1] = data[i];//把数据向前移动
}
count -- ;
return temp;
}
//根据索引取元素
public T GetElem(int i)
{
if(index > = 0 && index < = count - 1)
{
return data[index];
}
else
{
Console.WirteLine("索引不存在");
return default(T);//取得一个类型的默认值
}
}
//根据索引器取元素
public T this[int index]
{
get
{
return GetEle(index);
}
}
//根据值判断所在位置
public int Locate(T value)
{
for(int i = 0 ; i < count ; i ++)
{
if(data[i].Equal(value))
{
return i;
}
}
return -1;
}
}
这里为什么是IListDS是与.net自带IList相区别。对每个方法解释如下:
1、求长度:GetLength()
初始条件:线性表存在;
操作结果:返回线性表中所有数据元素的个数。
2、清空操作:Clear()
初始条件:线性表存在且有数据元素;
操作结果:从线性表中清除所有数据元素,线性表为空。
3、判断线性表是否为空:IsEmpty()
初始条件:线性表存在;
操作结果:如果线性表为空返回 true,否则返回 false。
4、附加操作:Append(T item)
初始条件:线性表存在且未满;
操作结果:将值为 item 的新元素添加到表的末尾。
5、插入操作:Insert(T item, int i)
初始条件:线性表存在,插入位置正确()(1≤i≤n+1,n 为插入前的表长)。
操作结果:在线性表的第 i 个位置上插入一个值为 item 的新元素,这样使得原序号为 i,i+1,…,n 的数据元素的序号变为 i+1,i+2,…,n+1,插入后表长=原表长+1。
6、删除操作:Delete(int i)
初始条件:线性表存在且不为空,删除位置正确(1≤i≤n,n 为删除前的表长)。
操作结果:在线性表中删除序号为 i 的数据元素,返回删除后的数据元素。删除后使原序号为 i+1,i+2,…,n 的数据元素的序号变为 i,i+1,…,n-1,删除后表长=原表长-1。
7、取表元:GetElem(int i)
初始条件:线性表存在,所取数据元素位置正确(1≤i≤n,n 为线性表的表长) ; 操作结果:返回线性表中第 i 个数据元素。
8、按值查找:Locate(T value)
初始条件:线性表存在。
操作结果:在线性表中查找值为 value 的数据元素,其结果返回在线性表中首次出现的值为 value 的数据元素的序号,称为查找成功;否则,在线性表中未找到值为 value 的数据元素,返回一个特殊值表示查找失败。
线性表的实现方式
线性表的实现方式有下面几种
1、顺序表
2、单链表
3、双向链表
4、循环链表
线性结构–顺序表
在计算机内,保存线性表最简单、最自然的方式,就是把表中的元素一个接一个的放进顺序的存储单元,这就是顺序表的顺序存储(Sequence Storage)。线性表的顺序存储是指在内存中用一块地址连续的空间依次存放线性表的数据元素,用这种方式存储的线性表叫顺序表(Sequence List)。顺序表的特点是表中相邻的数据元素在内存中存储位置也相邻。如图所示:
顺序表的存储
假设顺序表中每个数据元素占w个存储单元,设第i个数据元素的存储地址为Loc(ai),则有:Loc(ai) = Loc(a1) + (i-1)*w (1<=i <= n)式中Loc(a1)表示第一个数据元素a1的存储地址,也是顺序表的起始存储地址,成为顺序表的基地址(Base Address)。也就是说只要知道数据元素的基地址和每个数据元素的存储单元的个数就可以求出顺序表中任何一个数据元素的存储地址。并且,由于计算顺序表中的每个数据元素存储地址的时间相同,所以顺序表具有任意存取的特点,(可以再任意位置存取东西)
c#语言中的数组在内存中占用的存储空间就是一组连续的存储空间,因此,数组具有任意存取的特点。所以,数组天生具有表示顺序表的数据存储区域的特性。