实现一个简单的c++ list容器(含sort排序 链表归并算法实现)
2012-11-18 21:17
1166 查看
这个其实是很久之前写的,最近参加面试,复习C++,感觉不错,顺便翻出来整理了一遍。
使用过容器的都清楚,容器说白了其实就是一个类模板,它可以存放各种类型。而要对容器元素访问就要通过一个叫
迭代器的东西,迭代器在使用上和指针很相似。因此list容器的实现也主要是对类模板和迭代器的设计,当然也少不了
链表操作,因为list容器是通过链表来存放数据。
一、节点类
该类主要是存放容器内元素的数据(data)
二、迭代器类
迭代器的实现。看了下面的实现你或许就明白为什么说迭代器和指针很相似,因为它重载了指针的一些基本操作如 ‘*’,'->'等。
记录一个我当时一个很低级的错误:
在实现'->'重载时,我本来是通过间接访问节点数据:
但这显然是不行的,因为getData()返回的是一个临时变量,对其取到的地址,并非原节点数据的地址。因此要把迭代器类设为节点类的友元类,直接访问节点数据。
如下:
三、容器类
在这里实现一些基本的容器操作,如push_front()、push_back()、erase()、sort()等。重点讲一下sort()的实现也是花了我最多时间的地方。
本来想用冒泡直接来的,后来发现c++中list容器的sort()函数是通过归并算法实现的,因此我也采用归并DIY一个。
冒泡O(N^2),相比于归并最坏情形运行时间:O(NlogN),当问题规模变大时,冒泡显然是吃不消的。
首先是递归二分链表,递归到最后会把链表分为一个个长度为1的单独节点,然后再有序地往上归并这些节点,把长度为1的扩到2,2扩到4,4扩到8……(这
也是分治思想的精髓)。为了提高二分的效率,二分链表时使用了两个步长一快一慢的指针,快指针的遍历的速度是慢指针的两倍,这样当快指针遍历
到末结点时,慢指针刚好指在了链表的中间结点处。
为了对sort()函数的封装,就要使用函数指针了。容器内定义一个函数指针,测试程序写好cmp()排序方式函数,通过sort(cmp)调用,初始化容器内的
函数指针,使其指向cmp函数。(如果你没使用过STL中的sort()函数,可能你会不明白我在说什么。。)
四、测试程序
View Code
运行结果如下:
使用过容器的都清楚,容器说白了其实就是一个类模板,它可以存放各种类型。而要对容器元素访问就要通过一个叫
迭代器的东西,迭代器在使用上和指针很相似。因此list容器的实现也主要是对类模板和迭代器的设计,当然也少不了
链表操作,因为list容器是通过链表来存放数据。
一、节点类
该类主要是存放容器内元素的数据(data)
/* *节点类 */ template<typename elemType> class Node { private: elemType data; Node *next; public: Node():next(NULL) {} Node(elemType data):data(data),next(NULL) {} //类Iter、Mylist要设为友元 friend class Iter<elemType>; friend class Mylist<elemType>; };
二、迭代器类
迭代器的实现。看了下面的实现你或许就明白为什么说迭代器和指针很相似,因为它重载了指针的一些基本操作如 ‘*’,'->'等。
记录一个我当时一个很低级的错误:
在实现'->'重载时,我本来是通过间接访问节点数据:
elemType * operator ->()const {//重载迭代器->操作 return &(node.getData()); }
但这显然是不行的,因为getData()返回的是一个临时变量,对其取到的地址,并非原节点数据的地址。因此要把迭代器类设为节点类的友元类,直接访问节点数据。
如下:
elemType * operator ->()const {//重载迭代器->操作 return &(node->data); }
/* *迭代器类 */ template<typename elemType> class Iter { private: Node<elemType> *node; public: Iter(){} Iter(Node<elemType>* a):node(a){} elemType operator *() const {//*重载 return (node->data); } elemType * operator ->()const {//重载迭代器->操作 return &(node->data); } bool operator !=(Iter<elemType> &a)const {//重载迭代器!=操作 return node != a.node; } bool operator == (Iter<elemType> &a)const {//重载迭代器==操作 return node == a.node; } Iter<elemType> operator++(int) {//重载迭代器++作 Iter<elemType>tmp = *this; this->node = this->node->next; return tmp; } Node<elemType> * getNode(){ return node;} };
三、容器类
在这里实现一些基本的容器操作,如push_front()、push_back()、erase()、sort()等。重点讲一下sort()的实现也是花了我最多时间的地方。
本来想用冒泡直接来的,后来发现c++中list容器的sort()函数是通过归并算法实现的,因此我也采用归并DIY一个。
冒泡O(N^2),相比于归并最坏情形运行时间:O(NlogN),当问题规模变大时,冒泡显然是吃不消的。
首先是递归二分链表,递归到最后会把链表分为一个个长度为1的单独节点,然后再有序地往上归并这些节点,把长度为1的扩到2,2扩到4,4扩到8……(这
也是分治思想的精髓)。为了提高二分的效率,二分链表时使用了两个步长一快一慢的指针,快指针的遍历的速度是慢指针的两倍,这样当快指针遍历
到末结点时,慢指针刚好指在了链表的中间结点处。
为了对sort()函数的封装,就要使用函数指针了。容器内定义一个函数指针,测试程序写好cmp()排序方式函数,通过sort(cmp)调用,初始化容器内的
函数指针,使其指向cmp函数。(如果你没使用过STL中的sort()函数,可能你会不明白我在说什么。。)
/* *容器类 */ template<typename elemType> class Mylist { private: int _size;//容器长度 Node<elemType> *head;//指向头结点(不放数据) Node<elemType> *_end;//指向容器的最后一个元素 bool (*cmp)(elemType a,elemType b);//指针函数,指向给定排序方式的函数 public: Mylist() { head = new Node<elemType>(); head->next = NULL; _end = head; this->_size = 0; } ~Mylist() { Node<elemType> *p,*tem; p = head; while( p != NULL) { tem = p; p = p->next; delete tem; } } typedef Iter<elemType> iterator;//定义迭代器类型 void push_back(elemType data) {//在容器的尾部添加元素 _end->next = new Node<elemType>(data); _end = _end->next; _size++; } void push_front(elemType data) {//在容器的前端添加元素 Node<elemType> *p = new Node<elemType>(data); p->next = head->next; head->next = p; if(head == _end) _end = p; _size++; } int size() {//返回容器中的元素个数 return _size; } iterator begin() {//返回一个迭代器,它指向容器的第一个元素 iterator iter(head->next); return iter; } iterator end() {//返返回一个迭代器,它指向容器的最后一个元素的下一位置 iterator iter(_end->next); return iter; } bool erase(iterator iter) {//删除迭代器 iter 所指向的元素 Node<elemType> *p1 = iter.getNode(); Node<elemType> *p2 = head; while(p2->next != NULL) { if(p2->next == p1) { p2->next = p1->next; if(_end == p1) _end = p2; delete p1; p1 = NULL; return true; } p2 = p2->next; } return false; } void clear() {//清空容器 Node<elemType> *p,*tem; p = head->next; while( p != NULL) { tem = p; p = p->next; delete tem; } head->next = NULL; _end = head; } /*******以下采用归并算法实现了容器的排序操作*****/ void sort(bool (*cmp)(elemType ,elemType )); Node<elemType>* mergeSort(Node<elemType> *temHead); Node<elemType>* merge(Node<elemType> *first,Node<elemType> *second); }; /* *初始化排序函数指针 */ template<typename elemType> void Mylist<elemType>::sort(bool (*cmp)(elemType ,elemType )) { this->cmp = cmp; head->next=mergeSort(head->next); } /* *二分链表 */ template<typename elemType> Node<elemType>* Mylist<elemType>::mergeSort(Node<elemType> *temHead) { Node<elemType> *first; Node<elemType> *second; first=temHead; second=temHead; if(first==NULL||first->next==NULL) { //若只有一个节点直接返回(递归临界) return first; } while(second->next!=NULL && second->next->next!=NULL) { //利用一快一慢的指针把链表二分 first=first->next; //慢指针 second=second->next->next;//快指针 } if(first->next!=NULL) { second=first->next; first->next=NULL; first=temHead; } return merge( mergeSort(first),mergeSort(second) ); //递归二分各个子链表 } /* *归并两路链表 */ template<typename elemType> Node<elemType>* Mylist<elemType>::merge(Node<elemType> *first,Node<elemType> *second) {//注意到这里链表first,second已经是顺序的了 Node<elemType> *resList=new Node<elemType>(); //开辟一个临时头节点 Node<elemType> *current; current=resList; while(first!=NULL && second!=NULL) {//某一条链表空时结束 if((*cmp )(first->data,second->data)) {//根据函数指针来确定排序方式 current->next=first; current=current->next; first=first->next; } else { current->next=second; current=current->next; second=second->next; } } //把还剩下不空的链表继续接到临时头结点所在的链表 while(first!=NULL) { current->next=first; current=current->next; first=first->next; } while(second!=NULL) { current->next=second; current=current->next; second=second->next; } current = resList->next; delete resList;//记得释放头结点 return current; }
四、测试程序
View Code
#include<iostream> using namespace std; template<typename elemType> class Iter; template<typename elemType> class Mylist; /* *节点类 */ template<typename elemType> class Node { private: elemType data; Node *next; public: Node():next(NULL) {} Node(elemType data):data(data),next(NULL) {} //类Iter、Mylist要设为友元 friend class Iter<elemType>; friend class Mylist<elemType>; }; /* *迭代器类 */ template<typename elemType> class Iter { private: Node<elemType> *node; public: Iter(){} Iter(Node<elemType>* a):node(a){} elemType operator *() const {//*重载 return (node->data); } elemType * operator ->()const {//重载迭代器->操作 return &(node->data); } bool operator !=(Iter<elemType> &a)const {//重载迭代器!=操作 return node != a.node; } bool operator == (Iter<elemType> &a)const {//重载迭代器==操作 return node == a.node; } Iter<elemType> operator++(int) {//重载迭代器++作 Iter<elemType>tmp = *this; this->node = this->node->next; return tmp; } Node<elemType> * getNode(){ return node;} }; /* *容器类 */ template<typename elemType> class Mylist { private: int _size;//容器长度 Node<elemType> *head;//指向头结点(不放数据) Node<elemType> *_end;//指向容器的最后一个元素 bool (*cmp)(elemType a,elemType b);//指针函数,指向给定排序方式的函数 public: Mylist() { head = new Node<elemType>(); head->next = NULL; _end = head; this->_size = 0; } ~Mylist() { Node<elemType> *p,*tem; p = head; while( p != NULL) { tem = p; p = p->next; delete tem; } } typedef Iter<elemType> iterator;//定义迭代器类型 void push_back(elemType data) {//在容器的尾部添加元素 _end->next = new Node<elemType>(data); _end = _end->next; _size++; } void push_front(elemType data) {//在容器的前端添加元素 Node<elemType> *p = new Node<elemType>(data); p->next = head->next; head->next = p; if(head == _end) _end = p; _size++; } int size() {//返回容器中的元素个数 return _size; } iterator begin() {//返回一个迭代器,它指向容器的第一个元素 iterator iter(head->next); return iter; } iterator end() {//返返回一个迭代器,它指向容器的最后一个元素的下一位置 iterator iter(_end->next); return iter; } bool erase(iterator iter) {//删除迭代器 iter 所指向的元素 Node<elemType> *p1 = iter.getNode(); Node<elemType> *p2 = head; while(p2->next != NULL) { if(p2->next == p1) { p2->next = p1->next; if(_end == p1) _end = p2; delete p1; p1 = NULL; return true; } p2 = p2->next; } return false; } void clear() {//清空容器 Node<elemType> *p,*tem; p = head->next; while( p != NULL) { tem = p; p = p->next; delete tem; } head->next = NULL; _end = head; } /*******以下采用归并算法实现了容器的排序操作*****/ void sort(bool (*cmp)(elemType ,elemType )); Node<elemType>* mergeSort(Node<elemType> *temHead); Node<elemType>* merge(Node<elemType> *first,Node<elemType> *second); }; /* *初始化排序函数指针 */ template<typename elemType> void Mylist<elemType>::sort(bool (*cmp)(elemType ,elemType )) { this->cmp = cmp; head->next=mergeSort(head->next); while(_end->next != NULL) {//记得更新_end指向 _end = _end->next; } } /* *二分链表 */ template<typename elemType> Node<elemType>* Mylist<elemType>::mergeSort(Node<elemType> *temHead) { Node<elemType> *first; Node<elemType> *second; first=temHead; second=temHead; if(first==NULL||first->next==NULL) { //若只有一个节点直接返回(递归临界) return first; } while(second->next!=NULL && second->next->next!=NULL) { //利用一快一慢的指针把链表二分 first=first->next; //慢指针 second=second->next->next;//快指针 } if(first->next!=NULL) { second=first->next; first->next=NULL; first=temHead; } return merge( mergeSort(first),mergeSort(second) ); //递归二分各个子链表 } /* *归并两路链表 */ template<typename elemType> Node<elemType>* Mylist<elemType>::merge(Node<elemType> *first,Node<elemType> *second) {//注意到这里链表first,second已经是顺序的了 Node<elemType> *resList=new Node<elemType>(); //开辟一个临时头节点 Node<elemType> *current; current=resList; while(first!=NULL && second!=NULL) {//某一条链表空时结束 if((*cmp )(first->data,second->data)) {//根据函数指针来确定排序方式 current->next=first; current=current->next; first=first->next; } else { current->next=second; current=current->next; second=second->next; } } //把还剩下不空的链表继续接到临时头结点所在的链表 while(first!=NULL) { current->next=first; current=current->next; first=first->next; } while(second!=NULL) { current->next=second; current=current->next; second=second->next; } current = resList->next; delete resList;//记得释放头结点 return current; } bool cmp(int a, int b) {//从小到大排序 return a <= b; } int main(int argc, char** argv) { Mylist<int> test; Mylist<int>::iterator iter; for(int i = 0; i < 10; i++) { i < 5 ? test.push_back(i): test.push_front(i); } cout<<"未排序:"; for(iter = test.begin(); iter != test.end(); iter++) { printf("%d ", *iter); } cout<<endl; test.sort(cmp); cout<<"已排序:"; for(iter = test.begin(); iter != test.end(); iter++) { printf("%d ", *iter); } cout<<endl; return 0; }
运行结果如下:
相关文章推荐
- 链表的归并排序:来自STL_ list_ sort 算法
- 148 Sort List (归并实现链表排序)
- 一个简单链表的C++实现(二)
- 归并排序的简单实现(c++ 版本)
- 【LeetCode-面试算法经典-Java实现】【109-Convert Sorted List to Binary Search Tree(排序链表转换成二叉排序树)】
- 二路归并的一个C++最简单实现
- 归并排序的简单实现(c++ 版本)
- C++学习笔记26——泛型算法之容器元素排序(sort unique)
- 【LeetCode-面试算法经典-Java实现】【082-Remove Duplicates from Sorted List II(排序链表中删除重复元素II)】
- 转载的标准文档:C语言实现一个简单的单向链表list
- LeetCode-Sort List,链表排序(插入和归并),时间复杂度O(n^2) and O(nlgn)
- 【LeetCode-面试算法经典-Java实现】【083-Remove Duplicates from Sorted List(排序的单链表中删除重复的结点)】
- c++积累(1):一个简单的list容器迭代程序
- 将两个已排序的链表归并成一个链表(C++面试题)
- 怎样编写一个程序,把一个有序整数数组放到二叉树中? 编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
- C++ 中list容器,自定义sort排序规则,stl中sort自定义排序规则
- 链表排序Sort_List(归并)
- 一个简单的C++的链表实现(使用类模板)
- 【数据结构与算法基础】单链表及其应用基数排序 / Singly Linked List and radix sort
- 曾经遇到的一个面试题,快速排序用链表实现,算法和以前的相似,需要注意一些细节处理