1.线性表的定义
线性表(list):零个或者多个数据元素的有限序列。序列的含义即为元素之间是有顺序的,如果有多个元素,那么第一个元素没有前驱,最后一个元素没有后继,其他的元素都有且只有一个前驱和后继。有限的含义即为数据元素的个数是有限的,零个或者多个。
2.线性表的顺序存储结构
1.顺序存储结构定义
线性表的顺序存储结构:是用一段地址连续的存储单元依次存储线性表的数据元素。以顺序存储结构实现的线性表亦称顺序表。*
2.简单实现一个顺序表
-
C语言版本:
- CversionSqList.h头文件中定义如下:
#pragma once #ifndef _CversionSqList_H_ #define _CversionSqList_H_ #ifdef _WIN64 #else #include <unistd.h> #endif #include <stdio.h> #include <stdlib.h> #define MAXSIZE 20 //顺序表的容量,一般是不可变的的。 #define OK 0 #define ERROR 1 #define TRUE 1 #define FALSE 0 typedef int ElemType; //顺序表中的数据元素类型 typedef unsigned int Status; //函数返回的状态 //声明一个顺序表 typedef struct { ElemType data[MAXSIZE]; size_t length; //顺序表的长度,随着插入删除是可变的 }SqList; //初始化顺序表,建立一个空的表 void* initialList(SqList* list); //判断顺序表是否为空,如果为空返回true,否者返回false Status isEmptyList(SqList* list); //将顺序表清空 void clearList(SqList* list); //将顺序表销毁 void destroyList(SqList* list); //获取顺序表中第i个位置的数据元素 Status getElemList(size_t i, SqList* list, ElemType* e); //在顺序表中查找与给定元素值相等的元素,如果查找成功返回true否者返回false Status findElemList(ElemType e, SqList* list); //在顺序表中的第i个位置前插入数据元素e void insertList(SqList* list, ElemType e, size_t i); //删除顺序表中第i个位置的数据元素,并用e返回 void deleteList(SqList* list, ElemType* e, size_t i); //获取顺序表的当前长度即元素个数 Status getCurentLength(SqList* list); //正序遍历顺序表中的元素 void printSqList(SqList* list); //逆序顺序表中的元素并遍历 void reversePrintSqList(SqList* list); #endif
- CversionSqList.cpp中实现如下:
#include "CversionSqList.h" //初始化顺序表,建立一个空的表 void* initialList(SqList* list) { //错误码 int flag = 0; //分配内存 list = (SqList*)malloc(sizeof(SqList)); if (!list) { flag = -1; printf("function initialList() error:%d", flag); return nullptr; } list->length = 0; return list; } //判断顺序表是否为空,如果为空返回true,否者返回false Status isEmptyList(SqList* list) { if (list && list->length) { return FALSE; } return TRUE; } //将顺序表清空 void clearList(SqList* list) { list->length = 0; } //将顺序表销毁 void destroyList(SqList* list) { if (list) { list->length = 0; free(list); } } //获取顺序表中第i个位置的数据元素 Status getElemList(size_t i, SqList* list, ElemType* e) { if (i < 1 || i > list->length) { return ERROR; } *e = list->data[i - 1]; return OK; } //在顺序表中查找与给定元素值相等的元素,如果查找成功返回true否者返回false Status findElemList(ElemType e, SqList* list) { for (unsigned int i = 0; i < list->length; i++) { if (e == list->data[i]) { return TRUE; } } return FALSE; } //在顺序表中的第i个位置前插入数据元素e void insertList(SqList* list, ElemType e, size_t i) { int flag = 0; if (i < 1 || i > list->length + 1 || list->length == MAXSIZE) { flag = -2; printf("function insertList() error:%d\n", flag); return; } //不在顺序表的末尾插入就需要移动元素 if (i <= list->length) { for (unsigned int k = list->length; k >= i; k--) { list->data[k] = list->data[k - 1]; } } list->data[i - 1] = e; list->length++; } //删除顺序表中第i个位置的数据元素,并用e返回 void deleteList(SqList* list, ElemType* e, size_t i) { int flag = 0; if (i < 1 || i > list->length || list->length == 0) { flag = -3; printf("function deleteList() error:%d", flag); return; } *e = list->data[i - 1]; //不在顺序表末尾删除就需要移动元素 if (i < list->length) { for (unsigned int k = i; k < list->length; k++) { list->data[k - 1] = list->data[k]; } } list->length--; } //获取顺序表的当前长度即元素个数 Status getCurentLength(SqList* list) { if (!list) return ERROR; return list->length; } //正序遍历顺序表中的元素 void printSqList(SqList* list) { if (!list) return; ElemType temp = 0; for (unsigned int i = 0; i < getCurentLength(list); i++) { getElemList(i + 1, list, &temp); printf("%d\t", temp); } } //逆序顺序表中的元素并遍历 void reversePrintSqList(SqList* list) { if (!list) return; ElemType temp = 0; // 反转顺序表 for (unsigned int i = 0; i < (list->length) / 2; i++) { temp = list->data[i]; list->data[i] = list->data[list->length - i - 1]; list->data[list->length - i - 1] = temp; } printSqList(list); }
- 对上述实现的顺序表测试如下:
#include "CversionSqList.h" int main() { SqList* list = NULL; //初始化一个顺序表 list = (SqList*)initialList(list); //插入元素 for (unsigned int i = 0 ; i < MAXSIZE ; i ++) { insertList(list,i+1,i+1); } //已满的情况下再插入就会失败 insertList(list, 3, 3); //删除元素 ElemType temp = 0; deleteList(list,&temp,3); //正序遍历元素 printSqList(list); //逆序遍历元素 reversePrintSqList(list); //查找顺序表中某个位置的元素,0表示没有查找成功 int flag = findElemList(400,list); printf("flag:%d\n",flag); //清空线性表 clearList(list); printf("after clearList:the length of SqList is %d\n",getCurentLength(list)); //销毁线性表 destroyList(list); return 0; } //运行结果如下: //function insertList error : -2 //1 2 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 2 1 flag : 1 //after clearList :the length of SqList is 0
-
cpp版本:
- CppversionSqList.h头文件
#pragma once #ifndef _CPPVERSIONSQLIST_H_ #define _CPPVERSIONSQLIST_H_ #ifdef _WIN64 #pragma warning( disable : 4290 ) #endif #if __cplusplus > 201103L #warning "please degrade your compiler" #endif #include <iostream> #include <new> //for bad_alloc class #include <stdexcept> //for varid_argument class template<typename ElemType> class SqList { public: SqList()noexcept; SqList(size_t capacity)noexcept; SqList(size_t length, ElemType arr[]); //创建包含元素的顺序表 ~SqList()noexcept; void clearSqList(); //清空顺序表 void destroyList(); //销毁顺序表 void printSqList()const; //正序遍历顺序表 const ElemType& getElem(size_t i)const throw(std::invalid_argument); //获取i号位置的元素 bool pushFront(const ElemType& e); //头插法 bool pushBack(const ElemType& e); //尾插法 bool popFront(); //头删法 bool popBack(); //尾删法 //判断顺序表是否已满 inline bool isFull()const { return getCurentSize() >= getCurentCapacity(); } //判断顺序表是否为空 inline bool isEmpty()const { return getCurentSize() == 0; } //获取顺序表的当前元素大小 inline const size_t& getCurentSize()const { return _size; } //获取顺序表的容量 inline size_t getCurentCapacity()const { return _capacity; } private: ElemType* _pdata = nullptr; size_t _capacity; //顺序表的当前容量 size_t _size = 0; //顺序表的当前数据元素的个数 enum class DefaultCapacity { CAPACITY = 3 }; void initialMemberVar()noexcept; //初始化部分成员变量 bool insertSqList(size_t i, const ElemType& e); //再i号位置前插入元素e bool deleteSqList(size_t i); //删除i号位置的元素 }; //无参构造函数 template<typename ElemType> SqList<ElemType>::SqList()noexcept { _capacity =(size_t)DefaultCapacity::CAPACITY; initialMemberVar(); } //初始化成员变量的方法,初始化_size,_pdata template<typename ElemType> void SqList<ElemType>::initialMemberVar()noexcept { try { if (_capacity == 1) { _pdata = new ElemType; } else if (_capacity > 1) { _pdata = new ElemType[_capacity]; } _size = 0; } catch (const std::bad_alloc & var) { std::cout << var.what() << ":The memory is not allocated rightly" << std::endl; exit(-1); } } //构造函数重载,用于客户端自定义顺序表容量 template<typename ElemType> SqList<ElemType>::SqList(size_t capacity )noexcept { _capacity = capacity > (size_t)DefaultCapacity::CAPACITY ? capacity : (size_t)DefaultCapacity::CAPACITY; initialMemberVar(); } //创建含有数据元素的顺序表 template<typename ElemType> SqList<ElemType>::SqList(size_t length, ElemType arr[]) : SqList(length) { for (unsigned int i = 0; i < length; i++) { _pdata[i] = arr[i]; } _size += length; } //析构指针资源 template<typename ElemType> SqList<ElemType>::~SqList()noexcept { destroyList(); } //清空顺序表中的数据元素 template<typename ElemType> void SqList<ElemType>:: clearSqList() { _size = 0; } //销毁顺序表 template<typename ElemType> void SqList<ElemType>::destroyList() { if (_pdata) { if (getCurentCapacity() == 1) { delete _pdata; _pdata = nullptr; } else { delete[]_pdata; _pdata = nullptr; } } _size = 0; _capacity = 0; } //插入元素 template<typename ElemType> bool SqList<ElemType>::insertSqList(size_t i, const ElemType& e) { if (isFull()) { return false; } if (i < 1 || i > getCurentSize() + 1) { return false; } //不在尾部插入 if (i <= getCurentSize()) { for (unsigned int j = getCurentSize(); j >= i ; j --) { _pdata[j] = _pdata[j-1]; } } _pdata[i-1] = e; _size++; return true; } //删除元素 template<typename ElemType> bool SqList<ElemType>::deleteSqList(size_t i) { if (isEmpty()) { return false; } if (i < 1 || i > getCurentSize()) { return false; } //不在尾部删除元素 if (i < getCurentSize()) { for (unsigned int j = i ; j < getCurentSize(); j ++) { _pdata[j-1] = _pdata[j]; } } _size--; return true; } //打印顺序表元素 template<typename ElemType> void SqList<ElemType>::printSqList()const { for (unsigned int i = 0 ; i < getCurentSize(); i ++) { std::cout << getElem(i + 1); } std::cout << std::endl; } //获取i号位置的元素 template<typename ElemType> const ElemType& SqList<ElemType>::getElem(size_t i)const throw(std::invalid_argument) { if (i < 1 || i > getCurentSize()) { throw std::invalid_argument("invalid argument:incorrect index"); } return _pdata[i-1]; } //头插 template<typename ElemType> bool SqList<ElemType>::pushFront(const ElemType& e) { return insertSqList(1,e); } //尾插 template<typename ElemType> bool SqList<ElemType>::pushBack(const ElemType& e) { return insertSqList(getCurentSize() + 1,e); } //头删 template<typename ElemType> bool SqList<ElemType>::popFront() { return deleteSqList(1); } //尾删 template<typename ElemType> bool SqList<ElemType>::popBack() { return deleteSqList(getCurentSize()); } #endif
- 测试程序如下:
#include <string> #include "CppversionSqList.h" class Student { private: std::string _name; unsigned int _age; public: Student():_name(" "),_age(0) {} Student(const std::string& name, unsigned int age) :_name(name), _age(age) { } friend std::ostream& operator <<(std::ostream& out, const Student& s); }; std::ostream& operator <<(std::ostream& cout, const Student& s) { std::cout << "name:" << s._name << " " << "age:" << s._age << std::endl; return cout; } int main() { Student s1 ("张三",18); Student s2("李四", 20); Student s3("王五", 23); Student s4("流氓",45); SqList<Student>list(6); //头插,尾插 list.pushFront(s1); list.pushFront(s2); list.pushBack(s3); list.pushBack(s4); //遍历元素 list.printSqList(); //获取i号元素 /* Student temp1 = list.getElem(3); Student temp2 = list.getElem(4); //预图获取不合法位置的元素将调用abort函数中止程序运行 */ try { std::cout << list.getElem(3); std::cout << list.getElem(4); } catch (const std::invalid_argument& var) { std::cout << var.what() << std::endl; } //获取容量 std::cout << list.getCurentCapacity() << std::endl; //获取元素大小 std::cout << list.getCurentSize() << std::endl; //顺序表是否满或者空 std::cout << list.isFull() << list.isEmpty() << std::endl; //头删 list.popFront(); //尾删 list.popBack(); //再次遍历 list.printSqList(); //清空顺序表 list.clearSqList(); list.destroyList(); int arr[] = {12, 13, 14, 15}; SqList<int> ll(4, arr); ll.printSqList(); ll.destroyList(); return 0; } // 运行结果如下 // name:李四 age:20 // name:张三 age:18 // name:王五 age:23 // name:流氓 age:45 // name:王五 age:23 // name:流氓 age:45 // 6 // 4 // 00 // name:张三 age:18 // name:王五 age:23 // 12131415
-
上面简单实现了顺序表,但是使用的话标准库已经提供好了。比如说模板类vector亦称动态数组,模板类array亦称静态数组。
3.线性表使用顺序性存储结构的优缺点
- 优点有以下:
1.无需为表示表中元素间的逻辑关系而增加额外的存储空间。(数据元素之间的一对一的线性关系通过一段地址连续的存储单元体现出来了,这段存储单元既可以位于堆上也可以位于栈上)
2. 可以快速获取表中任意位置的数据元素。(获取操作的时间复杂度都为O(1)) - 缺点有以下:
- 除了尾部的插入和删除都需要移动大量的数据元素。(当在顺序表头部插入和删除时时间复杂度都达到了O(n),因此一般有的顺序表只提供尾部插入和删除操作)
- 当线性表长度变化较大时,难以确定存储空间的容量。(数据元素够多时,有可能预先分配好的容量不足存储,所以需要扩容)
- 可能造成存储空间的“碎片”。(数据元素不多时,有可能预先分配好了够多的容量造成空间的浪费)
3.线性表的链式存储结构
1.线性表的链式存储结构定义
- 线性表的链式存储结构:n个结点链接成为一个链表。链式存储结构实现的线性表亦称链表。
- 结点:存储一个数据元素直接后继位置的指针域和存储数据元素信息的数据域这两部分信息组成一个数据元素的存储映像叫做结点。依据节点中包含的指针域的个数来区分是单链表还是双链表。
2.单链表
-
单链表定义:链表中只包含一个指针域的链表叫做单链表
- 头指针:指向链表中第一个结点的存储地址,如果单链表中含有头结点,则存储指向头结点的存储地址。
- 头节点:单链表第一个结点前附设一个结点就叫做头节点。头结点的数据域可以不存储任何信息,也可以存储诸如线性表的长度等附加信息,头结点的指针域指向第一个结点。
- 头结点与头指针区别:
- 头指针:头指针具有标识作用,常用头指针冠宇链表的名字。无论链表是否为空,头指针均不为空。头指针是链表的必要元素。
- 头结点:头结点不一定是链表必要元素。但有了头结点,对于在第一节点前插入结点和删除第一结点就和其他结点的操作统一了。
-
简单实现一个单链表:
- 在CversionSingleList.h中:
#pragma once #ifndef _SINGLELIST_H_ #define _SINGLELIST_H_ #define OK 0 #define ERROR 1 #define TRUE 0 #define FALSE 1 #include <stdio.h> #include <stdlib.h> typedef int ElemType; typedef int status; //线性表的单链表存储结构 typedef struct Node { ElemType data; struct Node* next; }Node, * LinkList; //创建带有头结点的空单链表 LinkList createHeadList(LinkList* list); //在单链表中第i个结点前插入数据元素 status insertList(LinkList* list, int i, ElemType e); //头插法 status pushFront(LinkList* list, ElemType e); //删除单链表中第i个结点,并用e返回 status deleteList(LinkList* list, int i, ElemType* e); //用e返回单链表中第i个结点的数据元素 status getElem(LinkList list, int i, ElemType* e); //遍历单链表 void PrintLinkList(LinkList list); //将单链表重置为空表 status clearList(LinkList* list); //销毁单链表 status destroyList(LinkList * list); #endif
- 在CversionSingleList.cpp中:
#include "CversionSingleList.h" LinkList createHeadList(LinkList* list) { //创建一个头结点 *list = (LinkList)malloc(sizeof(Node)); if (!(*list)) { return nullptr; } (*list)->next = nullptr; //头结点的数据域可以不存储任何东西 (*list)->data = 0; return *list; } status insertList(LinkList* list, int i, ElemType e) { int j = 0; //计数器 LinkList current = nullptr, newNode = nullptr; current = *list; // 寻找第i-1个结点 while (current && j < i - 1) { current = current->next; j++; } if (j >= i || !current) { return FALSE; //没有找到第i-1个结点 } newNode = (LinkList)malloc(sizeof(Node)); if (!newNode) { return FALSE; } newNode->data = e; newNode->next = current->next; current->next = newNode; return TRUE; } status pushFront(LinkList* list, ElemType e) { return insertList(list, 1, e); } status deleteList(LinkList* list, int i, ElemType* e) { int j = 0; LinkList current = nullptr, temp = nullptr; current = *list; //寻找第i-1个数据节点 while (current && j < i - 1) { current = current->next; j++; } if (!current || !current->next || j >= i) return FALSE; temp = current->next; *e = temp->data; current->next = temp->next; free(temp); temp = NULL; return TRUE; } status getElem(LinkList list, int i, ElemType* e) { int j = 0; LinkList current = nullptr, s = nullptr; current = list; while (current && j < i) { current = current->next; j++; } if (!current || j > i) return FALSE; *e = current->data; return TRUE; } void PrintLinkList(LinkList list) { LinkList current = nullptr; current = list->next; while (current) { printf("%d\t", current->data); current = current->next; } printf("\n"); return; } /** * 销毁数据节点,头节点尚在 */ status clearList(LinkList* list) { if (!(*list)) return FALSE; LinkList current = nullptr, temp = nullptr; current = *list; while (current) { temp = current->next; current = temp->next; free(temp); temp = NULL; } return TRUE; } /** * 头节点销毁 */ status destroyList(LinkList* list) { free(*list); return TRUE; }
- 测试程序如下:
//C语言版 #include "CversionSingleList.h" int main() { LinkList list = nullptr; //创建头结点 list = createHeadList(&list); //头插法 pushFront(&list, 300); pushFront(&list, 200); pushFront(&list, 100); pushFront(&list, 90); pushFront(&list, 80); pushFront(&list, 70); //遍历单链表 PrintLinkList(list); //在i号位置前插入 insertList(&list, 3, 400); insertList(&list, 4, 500); //遍历单链表 PrintLinkList(list); //获取第i号位置的元素 ElemType temp = 0; int flag = getElem(list, 5, &temp); if (flag == 1) { printf("temp:%d\n", temp); } //删除i号位置的元素 deleteList(&list, 1, &temp); //遍历单链表 PrintLinkList(list); //清空单链表 clearList(&list); //销毁单链表 destroyList(&list); return 0; } //运行结果如下: // 70 80 90 100 200 300 // 70 80 400 500 90 100 200 300 // temp:90 // 80 400 500 90 100 200 300
-
作为类的设计者,需要考虑客户端的各种需求。那么设计出的单链表能够高效方便存储不同的数据类型的数据元素。显然,当客户端需要存储其他数据类型的数据元素时,上述实现的单链表需要修改宏定义比较繁琐。现对其改进如下:利用万能指针存储数据元素的地址,来满足用户不同数据类型的需求。对于单链表,我么们可以品读标准库forward_list类模板的具体实现。
- 在
CversionAdvancedSingleList.h
中定义如下:
#pragma once #ifndef _CVERSIONADVANCEDSINGLELIST_H_ #define _CVERSIONADVANCEDSINGLELIST_H_ #define TRUE 1 #define FALSE 0 #include <stdlib.h> #include <stdio.h> typedef int status; typedef struct Node { void* _data; //一个结点的数据域:一个万能指针指向用户的数据元素 struct Node* _next; //一个结点的指针域 }Node; typedef struct LinkList { Node* _pnode; //指向结点的指针 size_t _curentSize; //单链表中的数据元素个数 }LinkList; typedef void* ReturnType; //创建带头结点的空单链表 ReturnType createList(); //指定位置插入元素 size_t insertByPosion(size_t pos, void* data, LinkList** list); //删除指定位置的元素 size_t deleteByPosion(size_t pos,LinkList** list); //获取链表元素个数 int size(LinkList** list); //清空链表 size_t clearList(LinkList** list); //销毁链表 size_t destroyList(LinkList** list); //获取i号位置元素 ReturnType getElem(LinkList** list, int i); // 链表反转 status reverseList(LinkList** list); // 获取链表倒数第k个元素 ReturnType getInverseElem(LinkList** list, int k); size_t foreachList(LinkList * list,void (*foreachFunction)(void * data)); #endif //!_CVERSIONADVANCEDSINGLELIST_H_
- CversionAdvancedSingleList.cpp中实现如下:
#include "CversionAdvancedSingleList.h" //创建带头结点的空单链表 ReturnType createList() { //初始化头结点,并将当前size置为0 LinkList* list = (LinkList *)malloc(sizeof(LinkList)); if (!list) { return NULL; } list->_pnode = (Node *)malloc(sizeof(Node)); if (!list->_pnode) { return nullptr; } list->_pnode->_data = nullptr; list->_pnode->_next = nullptr; list->_curentSize = 0; return list; } //插入指定位置的元素 size_t insertByPosion(size_t pos, void* data, LinkList** list) { //数据为空或者单链表不存在 if (!data || !list) { return FALSE; } //位置不合法则对位置进行处理 if (pos < 1 || pos > size(list) + 1) { //如果位置不合法就在单链表尾部进行插入 pos = size(list) + 1; } Node * workNode = (*list)->_pnode; //找到pos-1号位置的结点 for (size_t i = 1 ; i < pos ; i++) { workNode = workNode->_next; } //创建新结点 Node* newNode = (Node*)malloc(sizeof(Node)); if (!newNode) { return FALSE; } newNode->_data = data; newNode->_next = workNode->_next; workNode->_next = newNode; //每新增一个结点,单链表的当前元素加一 (*list)->_curentSize++; return TRUE; } //删除指定位置的元素 size_t deleteByPosion(size_t pos, LinkList** list) { //单链表为空 if (!list || !(*list)->_pnode->_next) { return FALSE; } //位置不合法 if (pos < 1 || pos > size(list)) { return FALSE; } // 寻找第pos-1个位置的节点 Node* workNode = (*list)->_pnode; for (size_t i = 1 ; i < pos ; i++) { workNode = workNode->_next; } Node* temp = workNode->_next; workNode->_next = temp->_next; free(temp); temp = nullptr; (*list)->_curentSize--; return TRUE; } //获取链表元素个数 int size(LinkList** list) { //单链表不存在 if (!list) { return -1; } return (*list)->_curentSize; } /** * 正序遍历链表:如果客户需要使用正序遍历链表功能,则需要自定义遍历元素逻辑 */ size_t foreachList(LinkList * list,void (*foreachFunction)(void * data)) { //链表不存在或者客户没提供自定义遍历数据元素功能函数 if (!list || !foreachFunction) { return FALSE; } Node* workNode = list->_pnode->_next; int length = size(&list); for (int i = 0 ; i < length; i++) { foreachFunction(workNode->_data); workNode = workNode->_next; } return TRUE; } //清空链表:即数据节点 size_t clearList(LinkList** list) { //单链表不存在 if (!list) { return FALSE; } Node* workNode = (*list)->_pnode; //链表的数据结点不为空则需要清除 while (workNode->_next) { Node * temp = workNode->_next; workNode->_next = temp->_next; free(temp); temp = NULL; } return TRUE; } /** * 销毁链表,包括头节点 */ size_t destroyList(LinkList** list) { if (list && (*list)->_pnode) { free((*list)->_pnode); free(*list); *list = nullptr; } return TRUE; } ReturnType getElem(LinkList ** list, int i) { if (!list || !(*list)->_pnode || !(*list)->_pnode->_next) { return NULL; } if (i <= 0 || i > size(list)) return NULL; int j = 0; Node * current = (*list)->_pnode; while (j < i) { current = current->_next; j++; } if (j > i || !current) { return NULL; } return current->_data; } // 链表反转:使用双指针 status reverseList(LinkList** list) { if (!list || !(*list)->_pnode || !(*list)->_pnode->_next) { return FALSE; } Node* current = (*list)->_pnode->_next; Node* prev = nullptr; while (current) { Node* nextNode = current->_next; current->_next = prev; prev = current; current = nextNode; } // 更新头节点中的指针 (*list)->_pnode->_next = prev; return TRUE; } // 获取链表倒数第k个元素:使用快慢指针 ReturnType getInverseElem(LinkList** list, int k) { if (!list || !(*list)->_pnode || !(*list)->_pnode->_next) { return NULL; } if (k < 1 || k > size(list)) return NULL; Node* fast = (*list)->_pnode; Node* slow = (*list)->_pnode; // fast指针先走k步骤 for (int i = 0; i < k; ++i) { fast = fast->_next; } while (fast) { fast = fast->_next; slow = slow->_next; } return slow->_data; }
- 时间有限只实现了部分功能,测试程序如下:
#include "CversionAdvancedSingleList.h" #define _CRT_SECURE_NO_WARNINGS typedef struct Student { public: const char* _name; int _age; Student() :_name(" "), _age(-1) { } Student(const char * name,int age) :_name(name), _age(age) { } }Student; void foreach(void * var) { if (var) { Student* pstu = (Student*)var; printf("name:%s,age:%d\n", pstu-> _name, pstu->_age); } } int main() { Student s1 ("张三",14); Student s2 ("李四",15 ); Student s3 ("王五",16 ); Student s4 ( "二货",24 ); LinkList* list = (LinkList*)createList(); insertByPosion(1,&s1,&list); insertByPosion(1, &s2, &list); insertByPosion(2, &s3, &list); insertByPosion(4, &s4, &list); foreachList(list, foreach); deleteByPosion(1,&list); deleteByPosion(3, &list); foreachList(list, foreach); reverseList(&list); foreachList(list, foreach); for (int i = 0 ; i < size(&list) ; i++) { Student* temp = (Student*)getElem(&list, i+1); printf("name:%s,age:%d\n", temp->_name, temp->_age); } Student* temp = (Student*)getInverseElem(&list, 1); printf("name:%s,age:%d\n", temp->_name, temp->_age); clearList(&list); destroyList(&list); return 0; } //运行结果如下: // name:李四,age:15 // name:王五,age:16 // name:张三,age:14 // name:二货,age:24 // name:王五,age:16 // name:张三,age:14 // name:张三,age:14 // name:王五,age:16 // name:张三,age:14 // name:王五,age:16 // name:王五,age:16
- 在
3.单链表链式存储结构的优缺点
- 优点:
- 单链表采用链式存储结构,可以用任意位置的存储单元来存放线性表中的元素。(存储单元可连续也可以不连续)
- 单链表在找出某位置的指针后,插入和删除的时间仅为O(1),而顺序存储结构插入和删除时间为O(n).
- 单链表不用预先分配存储空间,只要可以分配,元素个数也不受限制
- 缺点:
- 单链表查找算法的时间复杂度为O(n),而顺序表的查找时间复杂度仅为o(1)。因此对于需要频繁查找并且删除插入操作较少的情况下宜采用顺序存储结构。
4.静态链表
- 静态链表:用数组描述的链表叫做静态链表,这种实现方法也叫做游标实现法。那么是如何描述的呢?数组的元素有两个数据域组成data和cur.数据域data用来存放数据元素。而游标cur相当于单链表中的next指针存放该元素的后继在数组中的下标。通常把未被使用的数组元素称为备用链表。数组第一个元素,即下标为0的元素的cur域存放备用链表的第一个节点的下标。而数组中的最后一个元素的cur则存放第一个有数据的元素的下标。
5.循环链表
循环链表:将单链表中终端节点的空指针改为指向头结点,这种头尾相接的单链表称为单循环链表简称循环链表。(circular linked list)。
6.双向链表(double linked list)
双向链表:双向链表是在单链表的每个节点中,再设置一个指向前驱结点的指针域。双向链表与单链表相比,对于某个节点的前一个节点进行操作,双向链表更方便快捷。
7.双向循环链表
双向循环链表:在双向链表的基础上,将终端节点的后继空指针改为指向头结点,将头结点的前驱空指针改为指向终端节点。
4.总结
总而言之,线性表分为顺序存储结构和链式存储结构。使用顺序存储结构的线性表又称之为顺序表,使用链式存储结构的线性表又称之为链表。话说到这,理解思想是很容易的 ,但兑现代码却很容易出错。因此往后的日子多加练习吧。