STL入门基础 模拟实现list容器

目录

双向带头链表实现

list容器的模拟实现


STL是C++标准库的重要组成部分,list容器在实际中非常的重要,它的常见的接口使用我们都要熟悉,这次肝一波list容器常见的接口使用,帮助大家深入理解

list容器的原理是双向带头循环链表,理解后再看list容器会更容易

双向带头链表实现

我们分文件编写,在头文件 List.h 进行声明,在 List.c 当中进行具体的函数定义,在 test.c 当中的做具体的测试实现

先来了解一下双向链表头文件 List.h 当中接口

#pragma once //防止重复包含
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//双向链表实现 
typedef int LDatetype;
typedef struct ListNode
{
  LDatetype date;//数据
  struct ListNode* prev;//
  struct ListNode* next;//

}LTNode;

LTNode* ListInit();//初始化

LTNode* BuyListNode(LDatetype x);//开辟新节点函数

void ListDestroy(LTNode* phead);//置空函数

void ListPrint(LTNode* phead);//打印函数

void ListPushBack(LTNode* phead, LDatetype x);//尾插

void ListPopBack(LTNode* phead);//尾删

void ListPushFront(LTNode* phead, LDatetype x);//头插

void ListPopFront(LTNode* phead);//头删

LTNode* ListFind(LTNode* phead, LDatetype x);//查找 

void ListInsert(LTNode* pos, LDatetype x);//指定位置插入 

void ListErase(LTNode* pos);//指定位置删除 

函数的定义方法非常重要,需要深入理解,下面详细学习在 List.c 当中具体的函数实现

初始化函数定义

LTNode* ListInit()//初始化
{
  //先开空间给哨兵位
  LTNode*phead = (LTNode*)malloc(sizeof(LTNode));
  phead->next = phead;//都指向自己
  phead->prev = phead;
  return phead;//采用返回值或二级指针
}

增加新节点函数定义

//开辟新节点函数
LTNode* BuyListNode(LDatetype x)
{
  //考虑当链表为空时,所以先开辟一个新的节点
  LTNode* newnode = 
	(LTNode*)malloc(sizeof(LTNode));
  if (newnode == NULL)
  {
   printf("malloc fail\n");
   exit(-1);
  }
  newnode->date = x;//插入数据
  newnode->next = NULL;
  newnode->prev = NULL;
     
  return newnode;//返回新节点
}

打印函数定义

每一个节点当中都会有一个next存储下一个节点的地址

void ListPrint(LTNode* phead)//打印函数
{
  assert(phead);
  //到phead结束
  LTNode* cur = phead->next;
  //此时cur是等于传过来指向头节点的指针
  //里面有一个数据存着第一个节点的地址
  while (cur != phead)
  {
    printf("%d ", cur->date);//使用指针获取头节点数据
    cur = cur->next;
    //每一个节点当中都会有一个next存储下一个节点的地址
    //通过地址就可以找到下一个节点
  }
  printf("\n");
}

尾部插入函数定义

尾是哨兵位的前一个

void ListPushBack(LTNode* phead, LDatetype x)//尾插
{
  assert(phead);
  LTNode* tail = phead->prev;//尾是哨兵位的前一个
  LTNode* newnode = BuyListNode(x);//调用上面开辟新节点函数
  //phead   tail    newnode 
  tail->next = newnode;//分别依次交换
  newnode->prev = tail;

  newnode->next = phead;
  phead->prev = newnode;
}

尾部删除函数定义

void ListPopBack(LTNode* phead)//尾删
{
  assert(phead);
  assert(phead->next != phead);//不能删掉哨兵位 
	
  //先找尾巴
  LTNode* tail = phead->prev;
  //要先给数据 在删除 

  LTNode* tailprev = tail->prev;//找一个指针记录tail
  free(tail);
  //
  tailprev->next = phead;
  phead->prev = tailprev;

  //ListErase(phead->prev)//可以复用指定删除
}

头部插入函数定义

void ListPushFront(LTNode* phead, LDatetype x)//头插
{
  //phead  newnode  next
  assert(phead);

  LTNode* newnode = BuyListNode(x);//

  LTNode* next = phead->next;

  //phead  newnode  next
  phead->next=newnode;
  newnode->prev = phead;

  newnode->next = next;
  next->prev = newnode;
}

头部删除函数定义

void ListPopFront(LTNode* phead)//头删
{
  assert(phead);
  assert(phead->next != phead);//不能删掉哨兵位 
  //phead  next  nextNext

  LTNode* next = phead->next;
  LTNode* nextNext = next->next;

  phead->next = nextNext;
  nextNext->prev = phead;
  free(next);//...

  //ListErase(phead->next);//可以复用指定删除
}

置空函数定义

置空函数一般会放在我们进行插入或删除的函数最后,释放我们在堆上申请的空间,将其还给操作系统,另外也会相应的进行检查越界等问题

void ListDestroy(LTNode* phead)//销毁链表
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
    LTNode* next = cur->next;//先保存下一个
    free(cur);//删除
    cur = next;//接着往后
  }
  free(phead);//在释放头节点
  phead = NULL;//传一级无法置空
}

查找某个节点位置

LTNode* ListFind(LTNode* phead, LDatetype x) //查找
{
  assert(phead);
  LTNode* cur = phead->next;
  while (cur != phead)
  {
    if (cur->date==x)
    {
	  return cur;
    }
    cur = cur->next;
  }
  return NULL;
}

指定位置之前插入

void ListInsert(LTNode* pos, LDatetype x)//指定插入
{
  //posprev  newnode  pos
  assert(pos);
  LTNode* posprev = pos->prev;//
  LTNode* newnode = BuyListNode(x);

  posprev->next = newnode;//注意顺序
  newnode->prev = posprev;

  newnode->next = pos;
  pos->prev = newnode;
}

删除指定位置

void ListErase(LTNode* pos)//指定删除
{
  assert(pos);
  LTNode* posprev = pos->prev;
  LTNode* posnext = pos->next;
  //posprev pos  posnext
  posprev->next = posnext;
  posnext->prev = posprev;
  free(pos);
  pos = NULL;

}

我们来用上面接口实现在 test.c 当中测试案例

//头尾插入删除 
void TestList1()
{
  LTNode* plist = ListInit();
  ListPushBack(plist, 1);//尾插1234
  ListPushBack(plist, 2);
  ListPushBack(plist, 3);
  ListPushBack(plist, 4);
  ListPrint(plist);//打印1 2 3 4
  ListPopBack(plist);//尾删两次
  ListPopBack(plist);
  ListPrint(plist);//打印1 2

  ListPushFront(plist, 1);//在头插1 2 3 4
  ListPushFront(plist, 2);
  ListPushFront(plist, 3);
  ListPushFront(plist, 4);
  ListPrint(plist);//打印4 3 2 1 1 2

  ListPopFront(plist);//头删一次

  ListPrint(plist);//打印3 2 1 1 2

  ListDestroy(plist);//置空函数 在最后
  plist = NULL;
}
//指定删除某个位置
void TestList2()
{
  LTNode* plist = ListInit();
  ListPushFront(plist, 1);//头插1 2 3 4
  ListPushFront(plist, 2);
  ListPushFront(plist, 3);
  ListPushFront(plist, 4);

  LTNode* pos = ListFind(plist, 2);//删除2
  if (pos)
  {
    ListErase(pos);
  }
  ListPrint(plist);//打印4 3 1

  ListDestroy(plist);//置空函数 在最后
  plist = NULL;

}
int main()
{
  TestList1();
  //TestList2();
}

list容器的模拟实现

模拟实现list容器可以帮助我们更好的理解底层迭代器是如何实现的

模拟实现函数的接口

#include<assert.h>
//模拟实现
template<class T>

struct list_node
{
	T _data;
	list_node<T>* _next;
	list_node<T>* _prev;

	//构造...
	list_node(const T& val = T())//
		:_next(nullptr)
		, _prev(nullptr)
		, _data(val)
	{}
};

//迭代器底层模拟
template<class T ,class Ref,class Ptr >//模板
struct _list_iterator//公有
{
	typedef list_node<T>Node;
	typedef _list_iterator<T,Ref,Ptr> self;//
	Node* _node;

	//迭代器构造
	_list_iterator(Node* node)//
		:_node(node)
	{}

	//取节点数据
	Ref operator*()
	{
		return _node->_data;
	}

	//重载箭头
	Ptr operator->()
	{
		//复用*
		return &_node->data;
	}

	//前置++
	self& operator++()//迭代器++返回迭代器
	{
		_node = _node->_next;
		return *this;
	}
	//后置++
	self& operator++(int)//迭代器++返回迭代器
	{
		self tmp = (*this);
		_node = _node->_next;
		return tmp;
	}

	//前置--
	self& operator--()//迭代器++返回迭代器
	{
		_node = _node->_prev;
		return *this;
	}
	//后置--
	self& operator--(int)//迭代器++返回迭代器
	{
		self tmp = (*this);
		_node = _node->_prev;
		return tmp;
	}

	//不相等
	bool operator!=(const self& it)
	{
		return _node != it._node;//注意用.
    }
	//相等
	bool operator==(const self& it)
	{
		return _node = it._node;//注意用.
	}
	//析构 节点不属于迭代器 不需要释放 链表浅拷贝
	//拷贝构造 默认生成的浅拷贝就可以 
};

//链表类
template<class T>
class list
{
	typedef list_node<T> Node;//私有数据	
public:
	typedef _list_iterator<T,T&,T*> iterator;//gongyou 普通迭代器
    //公有 const迭代器
	typedef _list_iterator<T,const T&,const T*> const_iterator;
		
	//普通迭代器
	iterator begin()//返回第一个节点迭代器
	{
		return iterator(_head->_next);
	}
	//
	iterator end()//返回哨兵位
	{
		return iterator(_head);
	}

	//const迭代器
	const_iterator begin()const//返回第一个节点迭代器
	{
		return const_iterator(_head->_next);
	}

	const_iterator end()const//返回哨兵位
	{
		return const_iterator(_head);
	}

	//无参构造
	list()
	{
		cout << "调用无参构造" << endl;
		_head = new Node();//
		_head->_next = _head;
		_head->_prev = _head;
	}
	//空初始化
	void empty_init()
	{
		_head = new Node();//
		_head->_next = _head;
		_head->_prev = _head;
	}

	//有参构造 现代写法 迭代器区间
	template <class InputIterator>
	list(InputIterator first, InputIterator last)
	{
		cout << "调用有参构造" << endl;
		_head = new Node();//
		_head->_next = _head;
		_head->_prev = _head;

		empty_init();
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}
	//交换函数
	void swap(list<T>& s)
	{
		std::swap(_head, s._head);//交换头指针
	}

	//拷贝构造
	//s2(s)
	list(const list<T>& s)
	{
		cout << "调用拷贝构造" << endl;
		empty_init();//先初始化 在和tmp交换
		list<T>tmp(s.begin(), s.end());
		swap(tmp);

	}
		
	//赋值
	//s2=s
	list<T>& operator=(list<T> s)
	{
		cout << "调用赋值" << endl;
		swap(s);
		return *this;

	}
	//析构
	~list()
	{
		cout << "调用析构" << endl;
		clear();//先挨着删除
		delete _head;//在释放头节点
		_head = nullptr;//哨兵位头节点置空
	}
	//清除
	void clear()
	{
		iterator it = begin();
		while (it != end())
		{
			it = erase(it);//
		}
	}

	//尾部插入
	void push_back(const T& x)
	{
		//找尾
		Node* tail = _head->_prev;//尾是哨兵位的前一个
		Node* newnode = new Node(x);//先申请新节点

		//_head   tail    newnode 
		tail->_next = newnode;
		newnode->_prev = tail;

		newnode->_next = _head;
		_head->_prev = newnode;

	}
		
	//头部插入
	void push_front(const T& x)
	{
		insert(begin(),x);
	}
	//尾删
	void pop_back()
	{
		erase(--end());
	}

	void pop_front()
	{
		erase(begin());
	}
	//指定位置插入 迭代器失效问题 pos之前
	iterator insert(iterator pos, const T& x)
	{
		Node* newnode = new Node(x);//申请新节点
		Node* cur = pos._node;//cur就是pos位置
		Node* posprev = cur->_prev;

		//prev   newnode  cur
		posprev->_next = newnode;
		newnode->_prev = posprev;

		newnode->_next = cur;
		cur->_prev = newnode;

		return iterator(newnode);//

	}

	//指定位置删除 
	iterator erase(iterator pos)
	{
		assert(pos != end());
		//找到pos后面和前面
		//连接后 在删除pos
		Node* cur = pos._node;//cur就是pos位置
		Node* posprev = cur->_prev;
		Node* posnext = cur->_next;

		//posprev cur   posnext
		posprev->_next = posnext;
		posnext->_prev = posprev;
		delete cur;//

		return iterator(posnext);//返回当前位置的下一个位置
	}
private:
	Node* _head;//哨兵位头节点
};

模拟实现的测试

测试要和上面模拟接口放到一个命名空间里面

//定义和实现
//const 迭代器  用模板控制
void print_list(const list<int>& s)
{

  list<int>::const_iterator it = s.begin();
  while (it != s.end())
  {
    //*it = 10;//const 迭代器不允许修改
    cout << *it << " ";
    ++it;
  }
  cout << endl;
}

//迭代器模拟测试
void test_list1()
{
  list<int>s;
  s.push_back(1);
  s.push_back(2);
  s.push_back(3);
  s.push_back(4);

  list<int>::iterator it = s.begin();
  while (it != s.end())
  {
    //*it = 20;//可修改
    cout << *it << " ";
    ++it;
  }
  cout << endl;
  print_list(s);
}

//头部 尾部删除测试
void test_list2()
{
   list<int>s;
   s.push_back(1);
   s.push_back(2);
   s.push_back(3);
   s.push_back(4);
   s.push_front(10);
   s.push_front(10);

   s.pop_back(); //尾部删除一个
   s.pop_front();//头部删除一个


   list<int>::iterator it = s.begin();
   while (it != s.end())
   {
    cout << *it << " ";//打印 10  1 2 3 
    ++it;
   }
   cout << endl;
}


//指定位置插入
void test_list3()
{

  list<int>s;
  s.push_back(1);
  s.push_back(2);
  s.push_back(3);
  s.push_back(4);
  s.push_back(5);
  //偶数前插入这个 偶数*10
  auto s1 = s.begin();
  while (s1 != s.end())
  {
   if (*s1 % 2 == 0)
   {
	s.insert(s1, *s1 * 10);
   }
    ++s1;
  }

  list<int>::iterator it = s.begin();
  while (it != s.end())
  {
   cout << *it << " ";
   ++it;
  }
   cout << endl;

}

//删除指定位置
void test_list4()
{
  list<int>s;
  s.push_back(1);
  s.push_back(2);
  s.push_back(3);
  s.push_back(4);
  s.push_back(5);
  s.push_back(6);
  s.push_back(7);
  //删除所有偶数
  auto s1 = s.begin();
  while (s1 != s.end())
  {
   if (*s1 % 2 == 0)
   {
	 //s.erase(s1);//注意删除 存在迭代器失效 
	 s1 = s.erase(s1);
   }
   else
   {
	 ++s1;
   }
  }

   for (auto e : s)
   {
    cout << e << " ";//打印 1357
   }
   cout << endl;

  s.clear();//清除
  s.push_back(1);
  s.push_back(2);


  for (auto e : s)
  {
    cout << e << " ";//打印 12
  }
  cout << endl;
}

//测试拷贝构造和赋值
void test_list5()
{
  list<int>s;
  s.push_back(1);
  s.push_back(2);
  s.push_back(3);
  s.push_back(4);

  list<int>s2;
  s2 = s;//调用赋值
  for (auto e : s2)
  {
   cout << e << " ";
  }
  cout << endl;
}

学习list一定要学会查看文档,这里放一个官方文档链接list官方文档,list容器在实际中非常的重要,我们只要熟悉常见的接口就可以

希望这篇文章大家有所收获,我们下篇见

  • 12
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值