一、线性表的逻辑结构
- 线性表的定义
1) 线性表(List)是由 n(n≥0)个相同类型的数据元素构成的有限序列。
2) 线性表的形式化定义为:线性表(List)简记为L,是一个二元组,
L = (D, R)
其中:D是数据元素的有限集合。
R是数据元素之间关系的有限集合。
在实际生活中线性表的例子很多。例如,1 到 100 的偶数就是一个线性表: (2,4,6,…,100)
3) 在一个复杂的线性表中,一个数据元素是一个记录,由若干个数据项组成,
含有大量记录的线性表又称文件(File)。例如,例子 1.1 中的学生信息表就是一个线性表,表中的每一行是一个记录。一个记录由学号、姓名、行政班级、性别和出生年月等数据项组成。
4) 在选择线性表的表示法之前,程序设计人员首先应该考虑这种表示法要支持
的基本操作。从线性表的定义可知,一个线性表在长度上应该能够增长和缩短,
也就是说,线性表最重要的操作应该能够在线性表的任何位置插入和删除元素。除此之外,应该可以有办法获得元素的值、读出这个值或者修改这个值。也应该可以生成和清除(或者重新初始化)线性表。
- 顺序表的定义
1) 线性表的顺序存储是指在内存中用一块地址连续的空间依次存放线性表的数据元素,用这种方式存储的线性表叫顺序表(Sequence List)
2) C#语言中的数组在内存中占用的存储空间就是一组连续的存储区域,因此,
数组具有随机存取的特点。所以,数组天生具有表示顺序表的数据存储区域的特性。
3) 对数组采用转置算法:
class Program
{
static void
{
int[]aa = new int[]{ 1, 2, 4, 5,7,8,9 };
Revose(aa);
for(int i = 0; i < aa.Length; i++)
{
Console.WriteLine(aa[i]);
}
}
public staticint[] Revose(int[] bb)
{
int temp = 0;
int len = bb.Length;
for (inti = 0; i < (bb.Length)/2; i++)
{
temp = bb[i];
bb[i]=bb[len-i-1];
bb[len-i-1]=temp;
}
return bb;
}
}
4)自定义顺序表示例代码:
using System;
using System.Collections.Generic;
using System.Text;
class Program
{
static void Main(string[] args)
{
SeqList<int> aa = new SeqList<int>(10);
aa.Append(11);
aa.Append(20);
aa.Append(220);
Console.WriteLine("未反转结果");
for (int i = 0; i < aa.Last + 1; i++)
{
Console.WriteLine(aa[i]);
}
aa.Reverse();
Console.WriteLine("反转后结果");
for (int i = 0; i < aa.Last+1; i++)
{
Console.WriteLine(aa[i]);
}
Console.WriteLine("查找20的位置");
Console.WriteLine(aa.Locate(20));
Console.WriteLine("是否为空");
Console.WriteLine(aa.IsEmpty());
Console.WriteLine("取第一个数据");
Console.WriteLine(aa.GetElem(1));
Console.WriteLine("删除第一个数据");
aa.Delete(1);
Console.WriteLine("删除后数据");
for (int i = 0; i < aa.Last + 1; i++)
{
Console.WriteLine(aa[i]);
}
}
}
public interface IListDS<T>
{
int GetLength(); //求长度
void Clear(); //清空操作
bool IsEmpty(); //判断线性表是否为空
void Append(T item); //附加操作
void Insert(T item, int i); //插入操作
T Delete(int i); //删除操作
T GetElem(int i); //取表元
int Locate(T value); //按值查找
}
public class SeqList<T> : IListDS<T>
{
private int maxsize; //顺序表的容量
private T[] data; //数组,用于存储顺序表中的数据元素
private int last; //指示顺序表最后一个元素的位置
//索引器
public T this[int index]
{
get
{
return data[index];
}
set
{
data[index] = value;
}
}
//最后一个数据元素位置属性
public int Last
{
get
{
return last;
}
}
//容量属性
public int Maxsize
{
get
{
return maxsize;
}
set
{
maxsize = value;
}
}
//构造器
public SeqList(int size)
{
data = new T[size];
maxsize = size;
last = -1;
}
//求顺序表的长度
public int GetLength()
{
return last+1;
}
//清空顺序表
public void Clear()
{
last = -1;
}
//判断顺序表是否为空
public bool IsEmpty()
{
if (last == -1)
{
return true;
}
else
{
return false;
}
}
//判断顺序表是否为满
public bool IsFull()
{
if (last == maxsize-1)
{
return true;
}
else
{
return false;
}
}
//在顺序表的末尾添加新元素
public void Append(T item)
{
if(IsFull())
{
Console.WriteLine("List is full");
return;
}
data[++last] = item;
}
//在顺序表的第i个数据元素的位置插入一个数据元素
public void Insert(T item, int i)
{
if (IsFull())
{
Console.WriteLine("List is full");
return;
}
if(i<1 || i>last+2)
{
Console.WriteLine("Position is error!");
return;
}
if (i == last + 2)
{
data[last+1] = item;
}
else
{
for (int j = last; j>= i-1; --j)
{
data[j + 1] = data[j];
}
data[i-1] = item;
}
++last;
}
//删除顺序表的第i个数据元素
public T Delete(int i)
{
T tmp = default(T);
if (IsEmpty())
{
Console.WriteLine("List is empty");
return tmp;
}
if (i < 1 || i > last+1)
{
Console.WriteLine("Position is error!");
return tmp;
}
if (i == last+1)
{
tmp = data[last--];
}
else
{
tmp = data[i-1];
for (int j = i; j <= last; ++j)
{
data[j] = data[j + 1];
}
}
--last;
return tmp;
}
//获得顺序表的第i个数据元素
public T GetElem(int i)
{
if (IsEmpty() || (i<1) || (i>last+1))
{
Console.WriteLine("List is empty or Position is error!");
return default(T);
}
return data[i-1];
}
//在顺序表中查找值为value的数据元素
public int Locate(T value)
{
if(IsEmpty())
{
Console.WriteLine("List is Empty!");
return -1;
}
int i = 0;
for (i = 0; i <= last; ++i)
{
if (value.Equals(data[i]))
{
break;
}
}
if (i > last)
{
return -1;
}
return i;
}
public void Reverse()
{
T tmp = default(T);
int len = GetLength();
for (int i = 0; i <= len / 2; ++i)
{
tmp = data[i];
data[i] = data[len - i-1];
data[len - i-1] = tmp;
}
}
}
- 单链表
1) 顺序表是用地址连续的存储单元顺序存储线性表中的各个数据元素,逻辑上
相邻的数据元素在物理位置上也相邻。因此,在顺序表中查找任何一个位置上的
数据元素非常方便,这是顺序存储的优点。但是,在对顺序表进行插入和删除时,
需要通过移动数据元素来实现,影响了运行效率。本节介绍线性表的另外一种存
储结构——链式存储(Linked Storage),这样的线性表叫链表(Linked List)。链表不
要求逻辑上相邻的数据元素在物理存储位置上也相邻,因此,在对链表进行插入
和删除时不需要移动数据元素,但同时也失去了顺序表可随机存储的优点。
2) 链表是用一组任意的存储单元来存储线性表中的数据元素(这组存储单元可
以是连续的,也可以是不连续的)。那么,怎么表示两个数据元素逻辑上的相邻
关系呢?即如何表示数据元素之间的线性关系呢?为此,在存储数据元素时,除
了存储数据元素本身的信息外,还要存储与它相邻的数据元素的存储地址信息。
这两部分信息组成该数据元素的存储映像(Image),称为结点(Node)。把存储据元
素本身信息的域叫结点的数据域(Data Domain),把存储与它相邻的数据元素的存
储地址信息的域叫结点的引用域(Reference Domain)。因此,线性表通过每个结
点的引用域形成了一根“链条”,这就是“链表”名称的由来。
- C#中的线性表
1) IList 接口表示一个集合,该集合中的项可被排序且可按索引任意访问。
2) 非泛型的 IList 接口的声明如下:
interface IList: ICollection,IEnumerable
{
//公有属性
bool IsFixedSize{get;} //只读,如果IList有固定大小,
//其值为true,否则为 false。
bool IsReadOnly{get;} //只读,如果IList是只读的,
//其值为true,否则为 false。
object this [Tindex] {get;set;} //索引器,得到或设置某个索引的项
//公有方法
int Add(object value); //添加某项到表中,返回被插入的新项
//在表中的位置。
void Clear(); //清空表。
int IndexOf(object value); //如果表中有与value 相等的项,
//返回其在表中的索引,否则,返回-1。
bool Contains(object value); //如果表中有与value 相等的项,
//返回true,否则为 false。
void Insert(int index,object value); //把值为value的项插入到索
//引为index的位置。
void Remove(object value); //删除表中第一个值为value 的项。
void RemoveAt(int index); //删除表中索引index处的项。
}
3) .NET框架中的一些集合类实现了 IList 接口,如ArrayList、ListDictionary、
StringCollection、StringDictionary。
4) ArrayList 类使用数组来实现 IList 接口,所以 ArrayList 可看作顺序表。
ArrayList 的容量可动态增长,通常情况下,当 ArrayList 中的元素满时,容量增加一倍,把原来的元素复制到新的空间中。当在 ArrayList 中插入一个元素时,该元素被添加到 ArrayList 的尾部,元素个数自动加 1。另外,需要注意的是,ArrayList中对元素的操作前提是 ArrayList 是一个有序表,但 ArrayList 本身并不一定是有序的。所以,在对 ArrayList 中的元素进行操作之前,应该对 ArrayList进行排序。关于排序的算法见第7 章。
5) ArrayList的使用例子:
class Program
{
static void
{
// 创建和初始化一个新的ArrayList.
ArrayList myAL = new ArrayList();
myAL.Add("Hello");
myAL.Add("World");
myAL.Add("!");
//显示ArrayList 的属性和值
Console.WriteLine("myAL");
Console.WriteLine("Count:{0}", myAL.Count);
Console.WriteLine("Capacity: {0}", myAL.Capacity);
Console.Write("Values:");
PrintValues(myAL);
}
public staticvoid PrintValues(IEnumerablemyList)
{
foreach(objectobj in myList)
{
Console.Write("{0}", obj);
Console.WriteLine();
}
}
}
6) 栈和队列是计算机中常用的两种数据结构,是操作受限的线性表。栈的插入
和删除等操作都在栈顶进行,它是先进后出的线性表。队列的删除操作在队头进行,而插入、查找等操作在队尾进行,它是先进先出的线性表。与线性表一样,栈和队列有两种存储结构,顺序存储的栈称为顺序栈,链式存储的栈称为链栈。顺序存储的队列称为顺序对列,链式存储的队列称为链队列。