最常用的数据结构有LIST、list、string、map。我们已经练习过LIST和string,现在练习list,双向链表。
注意双向链表有多种变形,比如非环结果双向链表,普通环行双向链表,多一个头节点的环行双向链表。不同的变形版本,对代码的复杂度有很微妙的影响。
typedef struct
{
//任何你认为需要的字段
} LIST;
提供如下形式的算法函数
1) LIST Create(void); 创建一个空链表,里面元素个数为0
LIST Create_n(int n);创建一个空链表,里面元素个数为n,每个元素都调用Init_T函数进行了赋初值。
LIST Create_n_t(int n, T t);创建一个空链表,里面元素个数为n,每个元素都赋初值为t。
void Destory(LIST *);销毁掉链表,回收内存
2) int Size(LIST *);获得LIST里的元素个数
3) T * Front(LIST *);获得LIST里的第一个元素的指针。请说明元素个数为0时,你的函数返回什么结果
T * Back(LIST *);获得LIST里的最后一个元素的指针。请说明元素个数为0时,你的函数返回什么结果
4) T * Begin(LIST *); T * End(LIST *);返回的2个指针构成半闭半开区间[begin, end)指示出链表里所有元素。
所谓半闭半开区间就是包括begin所指元素,但不包括end所指元素。半闭半开区间是数据使用上一个极重要的概念,因为它能表示空集合。配合下面的Next,你就可以在不管是否有元素的情况下都可简单地进行遍历了。
for (T * p = Begin(&v); p != End(&v); Next(&v, p))
{
//Do Something Here!
}
T * Next(LIST *, T *);假定传入的指针确保指向LIST里的某个元素,给出它的下一个元素的指针。请说明已经指向最后一个元素时,你的函数返回什么结果。
T * Prev(LIST *, T *);假定传入的指针确保指向LIST里的某个元素,给出它的前一个元素的指针。请说明已经指向第一个元素时,你的函数返回什么结果。
5) T * Find(LIST *, T t);搜索第一个值为t的元素,返回其指针。请说明没找到时,你的函数返回什么结果。
int Count(LIST *, T t);计算值为t的元素的个数
6) T * Insert(LIST *, T * pos, T t);将t插入到指针pos所指元素之前。返回新插入的元素的指针。
T * InsertRange(LIST *, T * pos, T * first, T * last);将[first, last)所指区间中所有元素保持原顺序插入到pos所指元素之前。返回新插入的第一个元素的指针。
void Push_Back(LIST *, T t);在链表最后追加一个元素。
void Push_Front(LIST *, T t);在链表最开头增加一个元素。
T * Erase(LIST *, T * pos);删除指针pos所指元素。返回被删除的元素的下一个元素的现在的指针。
T * EraseRange(LIST *, T * first, T * last);删除指针[first, last)所指区间中的元素。返回最后一个删除的元素的下一个元素的现在的指针。
void Pop_Back(LIST *, T t);删除最后一个元素。
void Pop_Front(LIST *, T t);删除最开头的那个元素。
void Clear(LIST *);清空所有元素。
BOOL Remove(LIST *, T t);删除所有值为t的元素。返回“是否进行了删除”。
7) void Assign(LIST * l, const LIST * r);使得l所指内存拥有和r完全相同的元素,而不是l.p和r.p指向相同的地址。注意内存的分配和释放。
void Swap(LIST * l, LIST * r);使得l拥有原来r的元素,r拥有l原来的元素。
LIST Create_byLIST(const LIST * v)//创建和v具有完全相同元素的新LIST
LIST Create_byArray(const T * first, const T * last)//创建和[first, last)所指数组区间相同的元素的新LIST
8) 附加题:
BOOL Empty(const LIST * v)//如果没有元素,返回TRUE,其它情况返回FLASE
void Assign_byArray(LIST * v, const T * first, const T * last)//使得v现在含有和[first, last)所指数组区间相同的元素
BOOL Equal(const LIST * l, const LIST * r)//当2个LIST元素个数并且按顺序,每个元素都相同时,返回TRUE
BOOL Less(const LIST * l, const LIST * r)//按字典序进行比较,l < r时返回TRUE。字典序就是 a开始的所有单词 < b开头的所有单词;a在所有a开头的单词里最小;其它a开头的单词根据第二个字母起继续比较。
9) 提高题:
实现Find、Count、Remove操作的扩展。
要能完成比如“Count(LIST *, XXXXXX);能计算所有值大于x的元素的个数”这样的通用问题。
10) 再提高题:
LIST用create函数创建后,因为分配了内存,所以必须用destory销毁的。而且每create一次,就得有且仅有一次对应的destory。
如果有如下代码LIST v1 = CREATE_n(2);
LIST v2 = CREATE_n(3);
v2 = v1;
那么将导致v2在create过程中分配的内存就泄漏了。
所以,问题是:如何能防止不小心的v2 = v1这类行为?
在你们的LIST代码里想办法完成,而不是指望使用LIST的人总不犯错。