数据结构学习笔记4——单链表的实现
2016-07-10 10:12
489 查看
线性表(list)的实现有两种标准方法:顺序表(array based list)和链表(linked list)。顺序表用数组实现,实现方法见上一节;链表用指针动态实现。链表又包括单链表(singly linked list)和双链表(doubly linked list)。本节讨论单链表的实现。
单链表的结构形式:
注意:
Head的Val中不存储具体的数值,而Tail的next不再指向任意节点。
Curr并非指向当前节点,而是指向当前节点的前一个节点,这样安排时为了便于实现插入操作(在当前节点前插入元素)
文件结构如下:
其中,Public.h、Tools.h、Tools.cpp为提供辅助功能的文件:Public.h包含常用的头文件,Tools.h和Tools.cpp实现了异常机制Assert();
List.h中实现线性表的抽象类模板,不包含数据成员,成员函数都是虚函数(当然构造函数除外);
Single_Linked_List.h和Single_Linked_List_Def.h实现单链表的模板类定义,其继承自List.h中的线性表模板;
main.cpp为主函数,实现单链表的调用和测试。
Lish.h的实现与前一节相同,实现线性表的抽象类模板:
Single_Linked_List.h:将整张单链表定义为一个类(模板)
Single_Linked_List_Def.h:实现Single_Linked_List.h中的成员函数定义
main.c 主函数,测试构造的单链表是否可用
运行结果:
单链表的结构形式:
注意:
Head的Val中不存储具体的数值,而Tail的next不再指向任意节点。
Curr并非指向当前节点,而是指向当前节点的前一个节点,这样安排时为了便于实现插入操作(在当前节点前插入元素)
文件结构如下:
其中,Public.h、Tools.h、Tools.cpp为提供辅助功能的文件:Public.h包含常用的头文件,Tools.h和Tools.cpp实现了异常机制Assert();
List.h中实现线性表的抽象类模板,不包含数据成员,成员函数都是虚函数(当然构造函数除外);
Single_Linked_List.h和Single_Linked_List_Def.h实现单链表的模板类定义,其继承自List.h中的线性表模板;
main.cpp为主函数,实现单链表的调用和测试。
Lish.h的实现与前一节相同,实现线性表的抽象类模板:
/********************************************************/ // 用模板实现线性表(List)的基类定义 /********************************************************/ #pragma once #include "Public.h" template<class dataType> class List { private: void operator= (const List&) {} //应该是无用的,可删除 List(const List&) {}//构造函数不可为虚函数 public: List() {} //默认构造函数,构造函数不可为虚函数 virtual ~List(){} //析构函数,基类一般总是需要虚析构函数,以便基类指针指向派生类时,删除基类指针时收回空间 // 清除内容,使线性表为空 virtual void clear() = 0; //纯虚函数,被派生类的实现覆盖,任何对象都不能调用当前版本 // 在当前位置插入元素 virtual void insert(const dataType& item) = 0; // 在末尾追加元素 virtual void append(const dataType& item) = 0; // 删除当前元素,返回其值 virtual dataType remove() = 0;//不能返回引用,因为该元素已经被删除了,只能返回值 // 将当前位置设为线性表的开始 virtual void moveToStart() = 0; // 将当前位置设为线性表的末尾 virtual void moveToEnd() = 0; // 将当前位置左移一格,若当前已经在开始位置则无改变 virtual void prev() = 0; // 将当前位置右移一格,若当前已经在结束位置则无改变 virtual void next() = 0; // 线性表中已经存储的元素的个数 virtual int length() const = 0; //size_t或unsigned int不是更好? // 获取当前的位置 virtual int currPos() const = 0; // 将当前位置的元素移动到某个特定位置 virtual void moveToPos(int pos) = 0; // 获取当前的元素 virtual const dataType& getValue() const = 0; //const引用 };
Single_Linked_List.h:将整张单链表定义为一个类(模板)
/********************************************************/ // 用模板实现单向链表(Single Linked List)的定义 // 继承基类线性表template<class dataType> class List /********************************************************/ #pragma once #include "List.h" // 1,定义节点类模板 template<class E> class Node { public: E element; //本结点存储的元素值 Node* next;//指向下一结点的指针 Node(const E& elemval, Node* nextval = NULL):element(elemval),next(nextval){} Node(Node* nextval = NULL) :next(nextval) {} }; // 2,定义链表类模板 template<class E> class Single_LList : public List<E> { private: Node<E>* head; //表头,head结点内并不存储真是的元素 Node<E>* tail; Node<E>* curr; //实际上curr->next才是真正的当前位置 int cnt; //链表中当前存储的元素个数 void init(); //初始化 void remove_all(); //清空 public: Single_LList(); ~Single_LList(); void print() const; void clear(); void insert(const E& it); //在当前位置插入元素 void append(const E& it); //在链表末尾追加 E remove(); //删除当前的元素并返回其值 void moveToStart(); //将当前位置设为链表开始 void moveToEnd(); //将当前位置设为链表结束 void prev(); //将当前位置左移一位 void next(); //将当前位置右移一位 int length() const; //返回当前链表存储的元素个数 int currPos() const; //返回当前位置 void moveToPos(int pos); //将当前位置设为指定位置 const E& getValue() const;//返回当前位置的元素值 };
Single_Linked_List_Def.h:实现Single_Linked_List.h中的成员函数定义
/********************************************************/ // 用模板实现单向链表(Single Linked List)的定义 // 继承基类线性表 template<class dataType> class List // 本头文件实现Single_LList<class E>的成员函数 /********************************************************/ #pragma once #include "Single_Linked_List.h" template<class E> void Single_LList<E>::init() { //所有指针指向新开的空间 head = tail = curr = new Node<E>; cnt = 0; } //删除所有结点 template<class E> void Single_LList<E>::remove_all() { while (head != NULL) { curr = head; //请注意,下面两句的顺序不能颠倒,不然curr删除后就相当于head删除了,就不能再获取head->next了 head = head->next; delete curr; } } //构造函数 template<class E> Single_LList<E>::Single_LList() { //构造时只开辟一个节点的空间 init(); } //析构函数 template<class E> Single_LList<E>::~Single_LList() { remove_all(); } //打印所有元素 template<class E> void Single_LList<E>:: print() const { Node<E>* temp = head->next; while (NULL != temp) { //此处暗示类型E必须定义了“<<”操作,否则报错 cout << temp->element << endl; temp = temp->next; } } //清空链表 template<class E> void Single_LList<E>::clear() { remove_all(); init(); //删除之后,还要留着表头 } //当前位置插入元素 template<class E> void Single_LList<E>::insert(const E& it) { //Node<E>* temp = curr->next; //curr->next = new Node<E>; //curr->next->element = it; //curr->next->next = temp; //这一句,代替了上面4句,调用了Node<E>的构造函数 curr->next = new Node<E>(it, curr->next); //若本来curr就在尾部,则插入后新元素成为新的尾部,需要更新tail指针 if (curr == tail) { tail = curr->next; //更新tail指针 } //不要忘记增加结点计数 cnt++; } //链表末尾追加元素 template<class E> void Single_LList<E>::append(const E& it) { tail->next = new Node<E>(it, NULL); tail = tail->next; cnt++; } //删除当前元素并返回其值 template<class E> E Single_LList<E>::remove() { Assert(curr != tail, "当前位置无效,无法删除"); Node<E>* temp = curr->next; E it = temp->element; if (temp == tail) //若删除的是末端元素,则需更新tail { tail = curr; } curr->next = temp->next; //即便是temp为tail的时候,这句依然成立 delete temp; cnt--; return it; } //将head设为当前位置 template<class E> void Single_LList<E>::moveToStart() { curr = head; } //将tail设为当前位置 template<class E> void Single_LList<E>::moveToEnd() { curr = tail; } //将当前位置左移一位,如果已在最前,无改变 template<class E> void Single_LList<E>::prev() { if (curr == head) { return; } Node<E>* temp = head; while (temp->next != curr) { temp = temp->next; } curr = temp; } //将当前位置右移一位,如果已在最后,无改变 template<class E> void Single_LList<E>::next() { if (curr == tail) { return; } curr = curr->next; } //返回链表长度,即 template<class E> int Single_LList<E>::length() const { return cnt; } //返回当前位置 template<class E> int Single_LList<E>::currPos() const { Node<E>* temp = head; int pos = 0; while (temp != curr) { pos++; temp = temp->next; } return pos; } //将当前位置设为指定位置 template<class E> void Single_LList<E>::moveToPos(int pos) { Assert((pos >= 0) && (pos <= cnt), "设定位置越界"); curr = head; for (int i = 0; i != pos; i++) { curr = curr->next; } } //返回当前位置的元素值 template<class E> const E& Single_LList<E>::getValue() const { Assert(curr != tail, "无值可取"); return curr->next->element; }
main.c 主函数,测试构造的单链表是否可用
/********************************************************/ // 主函数 // 用于测试编写的各函数与数据结构 /********************************************************/ #include "Public.h" #include "Tools.h" #include "SeqStack.h" #include "SeqStack_Vector.h" #include "SeqStack_VT_Def.h" #include "postfix.h" #include "ArrayBasedList_Def.h" #include "Single_Linked_List_Def.h" int main() { /********************************************************/ // 4,《数据结构与算法分析》Clifford 4.1.2单链表 /********************************************************/ Single_LList<int> iSLList; iSLList.insert(1); iSLList.insert(2); iSLList.insert(3); iSLList.insert(4); cout << "after insert" << endl; iSLList.print(); iSLList.clear(); cout << "after clear" << endl; iSLList.print(); }
运行结果:
相关文章推荐
- C#数据结构之顺序表(SeqList)实例详解
- Lua教程(七):数据结构详解
- 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解
- C#数据结构之队列(Quene)实例详解
- C#数据结构揭秘一
- C#数据结构之单链表(LinkList)实例详解
- 数据结构之Treap详解
- 用C语言举例讲解数据结构中的算法复杂度结与顺序表
- C#数据结构之堆栈(Stack)实例详解
- C#数据结构之双向链表(DbLinkList)实例详解
- JavaScript数据结构和算法之图和图算法
- Java数据结构及算法实例:冒泡排序 Bubble Sort
- Java数据结构及算法实例:插入排序 Insertion Sort
- Java数据结构及算法实例:考拉兹猜想 Collatz Conjecture
- java数据结构之java实现栈
- java数据结构之实现双向链表的示例
- Java数据结构及算法实例:选择排序 Selection Sort
- Java数据结构及算法实例:朴素字符匹配 Brute Force
- Java数据结构及算法实例:汉诺塔问题 Hanoi
- Java数据结构及算法实例:快速计算二进制数中1的个数(Fast Bit Counting)