本文包含以下内容:
一、线性表的介绍以及抽象ADT
二、顺序表及顺序表的实现
三、链表及链表的实现
四、顺序表和链表的比较
五、总结
参考书目:《数据结构与算法分析》【美】Clifford A.Shaffer著
一、线性表的介绍以及抽象ADT
1.什么是线性表?
关键字:线性、有序、有限。
所谓线性,是一个元素接着一个元素,就像一条线那样排放。而不是离散散乱的,或者一堆一堆的。
所谓有序,不是说表中的元素要按照从小到大或者从大到小排列,而是说每个元素都有它自己的位置
所谓有限,表中的元素个数不能是无穷多。
2.线性表应该有什么样的属性和操作
属性:表头、表尾、长度。
操作:增删改查
线性表中最重要的概念应该是当前位置,一切的操作都是对当前位置的操作。
比如getvalue()得到当前位置的值,insert()在当前位置添加元素,remove(),移除当前位置的元素
3.抽象ADT
template
class List {
private:
void operator = (const List&) {};
List(const List&);
public:
List() {};
virtual ~List() {};
virtual void clear()=0;
virtual void insert(const E&it)=0;
virtual void append(const E&it)=0;
virtual E remove()=0;
virtual void moveToStart() =0;
virtual void moveToEnd()=0;
virtual void pre()=0;
virtual void next()=0;
virtual int length()const = 0;
virtual int currPos()const = 0;
virtual void moveToPos(int pos) = 0;
virtual const E& getValue()const {};
};
常量引用参数、常量函数、常量引用返回值
常量引用参数:
virtual void insert(const E&it)=0;
virtual void append(const E&it)=0;
常量引用确保函数在操作过程中不会改变被引用参数的值
常量函数
virtual int length()const = 0;
virtual int currPos()const = 0;
virtual int length()const = 0;
virtual int currPos()const = 0;
一个对象的所有成员函数会返回一个指向该对象的隐含this指针,对于常成员函数,返回的是一个常this指针。
virtual int length()const = 0;
virtual int currPos()const = 0;
virtual const E& getValue()const {};
如果一个常成员函数想返回其对象的引用,则应该返回一个常引用。
换句话说,如果一个常函数,它返回的内容是this对象的一部分,则它应该返回一个常引用。
二、顺序表及顺序表的实现
1.什么是顺序表?
顺序表是通过数组来实现的线性表
2.顺序表应该具有那些属性?
maxsize,length,curr以及一个数组指针
3.顺序表的实现
#include
#define default size 100
template
class AList {
private:
E* list;
int lenght;
int maxsize;
int curr;
public:
AList(int size = defaultsaize){
maxsize = size;
length = 0;
curr = 0;
list = new E[maxsize];//
}
~AList() {
delete[] list;
}
void clear(){
delete[] list;
length = curr = 0;
list = new E[maxsize];
}
void moveToStart() {
curr = 0;
};
void moveToEnd() {
if (length > 0)curr = length - 1;
else
curr = 0;
};
void pre() {
if (curr > 0)curr = curr - 1;
};
void next() {
if (curr + 1 < maxsize)curr + curr + 1;
};
int insert(const E&item){
assert(length < maxsize, "数组越界");
//把curr右边的数全部往右移一位
for (int i = length; i > curr; i--)
list[i] = list[i - 1];
list[curr] = item;
length++;
return item;
}
int remove(){
//把curr右边的数往左移一位
assert(curr>=0&&curr= 0 && curr < length, "No element");
return list[curr];
}
};
三、链表及链表的实现
1.什么是链表?
使用一个个独立的结点来实现的线性表,每个独立的结点都包含当前结点的值以及指向下一个元素的指针。
链表强调的是相对位置
2.链表应该有哪些属性
length,指向头部的头指针head,指向当前结点的指针curr(实际上该指针指向的是当前结点右边的一个结点,后面会在讲解原因)
为了能在常数时间里实现对尾部的访问,我们还需要一个指向尾部结点的 tail指针
当链表为空的时候,没有元素可以让head curr tail来指向,在 插入或删除函数中会需要一些特别的处理代码,为了简便和减少出现错,
我们引进一个特殊的头结点,该节点表示表头,没有值,指针指向下一个元素, 链表为空时,表头结点的指针指向NULL
3.链表的实现
template
class node {
public:
E element;
node* next;
node() { next = NULL; }
node(const E& Element, node*Next = NULL) {
element = element;
next = Next;
}
};
template
class LList{
private:
node* curr;
node*head;
node*tail;
int length;
void Init(){
tail = curr = head = new node;
length = 0;
}
void RemoveAll(){
while (head != NULL) {
curr = head;
head = head->next;
delete curr;
}
}
public:
LList() { Init(); }
~LList() { RemoveAll(); }
void clear() {
RemoveAll();
Init();
}
void moveToStart(){
assert(head != NULL, "There is no element");
curr = head;
}
void moveToEnd(){
assert(tail != NULL, "There is no element");
curr = tail;
}
void pre(){
assert(head->next != NULL, "There is no pre element");
node* temp = head;
while (temp->next != curr)
temp = temp->next;
curr = temp;
}
void next(){
assert(curr->next != NULL, "There is no next ellement");
curr = curr->next;
}
void insert(const E&item){
curr->next = new node*(item, curr->next);
if (tail == curr)tail = curr->next;
length++;
}
void remove(){
assert(curr->next != NULL, "error");
if (tail == curr->next)tail = curr;
node* temp = curr->next;
curr->next = temp->next;
delete temp;
length--;
}
void append(const E&item){
tail->next = new node*(item, NULL);
tail = tail->next;
length++;
}
int getCurrPos()const{
int cnt = 0;
node* temp = head;
while (temp != curr)
{
temp = tem->next;
cnt++;
}
return cnt;
}
int getLength()const{
return length;
}
const E&getValut()const{
assert(curr ->next!=NULL, "no element");
return curr->next->element;
}
};
四.顺序表和链表的比较
访问任意位置,顺序表的 时间是常数,链表则不一定
任意位置插入元素,链表的时间是常数,线性表不一定
空间上,链表的指针往往会占用更多的空间,但是链表的长度不用一开始固定,方便 。而线性表要给他一个maxsize,超过maxsize的部分 是不能使用的。
五.总结
复习了线性表了、链表、顺序表
遗忘的 知识:常引用参数、常成员函数、常引用返回值
注意:const总是对它左边的起限定作用,除非它自身已经在最左。