链表(上)LinkList、StaticLinkList、CircleList

线性表的链式存储结构

  顺序表:基于顺序存储结构的线性表
  链表:基于链式存储结构的线性表

  顺序表与链表的差异
    时间复杂度:
      对于内置基础类型,顺序表和单链表的效率不相上下
      对于自定义类类型,顺序表在效率上低于单链表
      
    插入和删除 :
      顺序表:涉及大量数据对象的复制操作
      单链表:只涉及指针操作,效率与数据对象无关
      
    数据访问:
      顺序表:随机访问,可直接定位数据对象
      单链表:顺序访问,必须从头访问数据对象,无法直接定位    

  链表大家族:
    -单链表:每个结点只包含直接后继的地址信息
    -循环链表:单链表中的最后—个结点的直接后继为第—个结点
    -双向链表:单链表中的结点包含直接前驱和后继的地址信息
    -双向循环链表:双向链表中的第一个节点的直接前驱为最后一个节点,最后一个节点的直接后继为第一个节点
    -十字循环链表…

链表家族的继承结构

  创建首结点,用于指向第一个结点,辅助数据元素的定位。
  首结点仅启标识作用,并不是链表中的一个数据结点。

  头结点若采用Node直接创建,存在隐患,解决方案:

#pragma pack(4)
    /*
        如果T中成员最大元素为8字节(含有Double),Node将以8字节进行内存对齐
        下面的m_header是4字节对齐,内存布局会存在差异
        retinterpret_cast转换得来的数据将会错误(内存对齐方式相同才可以强制类型转换)
        所以强制使用4字节对齐
    */
	struct Node : public Object
	{
		T value;
		Node* next;
	};
	/*
		不直接使用Node*声明(使用sizeof),是为了避开泛指类型T的构造,因为T的构造可能会发生异常
        都继承自顶层父类,保证内存布局与node相同,就可以通过retinterpret_cast强制转换来使用
		mutable的声明是因为const成员函数中会取m_header的地址
	*/
	mutable struct : public Object
	{
		char reserved[sizeof(T)];
		Node* next;
    }m_header;
#pragma pack()

  插入内存布局知识:

/*      内存布局小剧场 toStudy
1.结构体的总大小,必须要是其内部最大成员的整数倍,不足的要补齐。
2.结构体或联合的数据成员,第一个数据成员是要放在offset == 0的地方,
  如果遇上子成员,要根据子成员的类型存放在对应的整数倍的地址上。
3.如果结构体作为成员,则要找到这个结构体中的最大元素,然后从这个最大
  成员的整数倍地址开始存储。
*/
typedef struct one {
    char a;//=====> 1 -> 4
    int b;//======> 4
    double c;//===> 8. //max
    char d;//=====> 1 -> 8  //补齐到8的整数倍
} ONE;//sum ======> 24

typedef struct two {
    char array[2];//==> 2 -> 4
    int b;//==========> 4
    double c;//=======> 8  //max
    float d;//========> 4 -> 8  //原则1
} TWO;//sum ==========> 24

typedef struct three {
    char a;//=====> 1 -> 4
    int b;//======> 4
    double c;//===> 8
    short d;//====> 2 -> 8  //原则3 ,下面是个结构体,其中最大成员为8,则需要从8的整数倍地址存放,所以变量d补齐到8
    TWO e;//======> 24 (max 8)
}THREE;//sum =====> 48

LinkList

  LinkList的设计要点:
    使用类模板技术,通过首结点访问后继结点
    定义内部结点类型Node,实现数据域与指针域
    封装内存空间的申请与删除,提高可拓展性
    提供游标与步长 封装move();end();next();current(),使得链表可以按步长线性访问
    使用举例:for(move(0);!end();next()) list.current(); //需要先move,才可以使用

LinkList.h

#ifndef _LinkList_H_
#define _LinkList_H_
#include "include/List.h"
#include "include/Exception.h"

namespace JYlib
{
/*				单链表
创建新的堆空间,将对象的值拷贝进去,所以原对象改变与链表无关
链表中的对象改变,也不影响原对象
*/
template < typename T >
class LinkList:public List<T>
{
protected:
#pragma pack(4)
    /*
        如果T中成员最大元素为8字节(含有Double),Node将以8字节进行内存对齐
        下面的m_header是4字节对齐,内存布局会存在差异
        retinterpret_cast转换得来的数据将会错误(内存对齐方式相同才可以强制类型转换)
        所以强制使用4字节对齐
    */
	struct Node : public Object
	{
		T value;
		Node* next;
	};
	/*
		不直接使用Node*声明(使用sizeof),是为了避开泛指类型T的构造,因为T的构造可能会发生异常
        都继承自顶层父类,保证内存布局与node相同,就可以通过retinterpret_cast强制转换来使用
		mutable的声明是因为const成员函数中会取m_header的地址
	*/
	mutable struct : public Object
	{
		char reserved[sizeof(T)];
		Node* next;
    }m_header;
#pragma pack()
	
	int m_length;

	Node* m_current;//游标当前位置

	int m_step;//游标步进的长度

	//定位对象的前一个位置
	Node* position(int i)const
	{
		Node* ret = reinterpret_cast<Node*>(&m_header);
		for(int j = 0;j < i;j++)
		{
			ret = ret->next;
		}
		return ret;
	}

	//封装申请与删除 增强拓展性
	virtual Node* create()
	{
		return new Node();
	}

	virtual void destroy(Node* pn)
	{
		delete pn;
	}
public:
	LinkList()
	{
		m_header.next = NULL;
		m_current = NULL;
		m_step = 1;
		m_length = 0;
	}

    LinkList(const List<T>& t) {}

    LinkList& operator =(const List<T>& e) {}

	bool insert(const T& e)
	{
		return insert(m_length,e);
	}

	bool insert(int i,const T& e)
	{
		bool ret = (0 <= i)&&(i <= m_length);//小于等于是为了可以在末尾插入
		if(ret)
		{
			Node* new_Node = create();

			if(new_Node != NULL)
			{
				Node* current = position(i);

				new_Node->value = e;
				new_Node->next = current->next;
				current->next = new_Node;

				m_length++;
			}
			else
			{
				THROW_EXCEPTION(NoEnoughMemoryException,"No memory to create DynamicList object ...");
			}
		}
		return ret;
	}

    //remove时同时析构掉对象
	bool remove(int i)
	{
		bool ret = ((0 <= i)&&(i < m_length));
		if(ret)
		{
			Node* current = position(i);
			Node* remove = current->next;

			if(m_current == remove)//如果当前游标在此,则游标指向下一个位置
			{
				m_current = remove->next;
			}

			current->next = remove->next;

			m_length--;
			destroy(remove);//先改数据,后析构,即使析构出问题,其仍然已经不是单链表一员
		}
		return ret;
	}

	bool set(int i,const T& e)
	{
		bool ret = (0 <= i)&&(i < m_length);
		if(ret)
		{
			position(i)->next->value = e;
		}
		else
		{
			THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");//抛出数组越界异常
		}
		return ret;
	}

	bool get(int i,T& e)const
	{
		bool ret = (0 <= i)&&(i < m_length);
		if(ret)
		{
			e = position(i)->next->value;
		}
		else
		{
			THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");//抛出数组越界异常
		}
		return ret;
	}
	
	virtual T get(int i)const
	{
		T ret;

		get(i,ret);

		return ret;
	}

	int find(const T& e)const
	{
		int ret = -1;
		int i = 0;
		Node* node = m_header.next;
		
		while(node != NULL)
		{
			if(node->value == e)
			{
				ret = i;
				break;
			}
			else
			{
				i++;
				node = node->next;
			}
		}
		return ret;
	}

	int length(void)const
	{
		return m_length;
	}

	//游标移动到目的地址,并重设置步进值
	virtual bool move(int i,int step = 1)
	{
		bool ret = (0 <= i)&&(i < m_length)&&(step > 0);
		if(ret)
		{
			m_current = position(i)->next;
			m_step = step;
		}
		//else
		//{
		//	THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");//抛出数组越界异常
		//}
/*
此处不需要异常的原因为,当使用封装好的,move,next,end等操作时
如果链表里没有元素,for(move(0);!end();next()),将会直接抛出异常
那么使用这个语句前还要判断是否有元素存在 if(length() != 0)
使得本来简易的for循环使用起来容易出问题
*/
		return ret;
	}

	//游标是否到末尾
	virtual bool end()
	{
		return (m_current == NULL);
	}

	//当前游标的值
	virtual T current()
	{
		if(!end())
		{
			return m_current->value;
		}
		else
		{
			THROW_EXCEPTION(IndexOutOfBoundsException,"Parameter i is invalid ...");//抛出数组越界异常
		}
	}
	
	//步进至一个游标
	virtual bool next()
	{
		int i = 0;
		while((i < m_step)&&(!end()))
		{
			m_current = m_current->next;
			i++;
		}
		return (i == m_step);
    }

	void clear()
	{
		while(m_header.next)
		{
			Node* remove = m_header.next;

			m_header.next = remove->next;

			m_length--;

			destroy(remove);
		}
	}

	~LinkList()
	{
		clear();
	}
};

}


#endif

StaticLinkList

  静态单链表的由来:
    当长时间使用单链表对象频繁增加和删除数据元素时
    堆空间会产生大量的内存碎片,导致系统运行缓慢
    想法:在给定的大小空间内,进行增加或删除结点数据
    
    实现:
      在类中定义固定大小的空间(unsigned char[])
      增加标记数组,标记对应的空间是否被使用
      重写create和destroy函数,改变内存的分配和归还方式
      在Node类中重载operator new , 用于在指定内存上创建对象

  数据结点重新定义:

    //使用LinkList<T>::Node访问LinkList中的Node typename说明Node是类型而不是数据
	typedef typename LinkList<T>::Node Node;

	//重载new操作符,因为如果是对象,仅分配地址不行,还需要调用对象的构造函数
	struct SNode : public Node 
	{
		//new(loc)type; loc为new的目标地址,type会被内置的sizeof转化
		//new操作符重载,第一个是sizeof转出来的内存大小,第二个参数是存放地址
		void* operator new(unsigned int size,void* loc)
		{
			(void)size;//不使用会报对象未使用的warning
			return loc;
		}
	};

StaticLinkList.h

#ifndef _StaticLinkList_H_
#define _StaticLinkList_H_
#include "LinkList.h"

namespace JYlib
{

template < typename T,int N >
class StaticLinkList:public LinkList<T>
{
protected:
    //使用LinkList<T>::Node访问LinkList中的Node typename说明Node是类型而不是数据
	typedef typename LinkList<T>::Node Node;

	//重载new操作符,因为如果是对象,仅分配地址不行,还需要调用对象的构造函数
	struct SNode : public Node 
	{
		//new(loc)type; loc为new的目标地址,type会被内置的sizeof转化
		//new操作符重载,第一个是sizeof转出来的内存大小,第二个参数是存放地址
		void* operator new(unsigned int size,void* loc)
		{
			(void)size;//不使用会报对象未使用的warning
			return loc;
		}
	};

	unsigned char m_space[sizeof(SNode) * N];//静态申请N个SNode大小的空间

	int m_used[N];//标记空间是否使用

	virtual Node* create()
	{
		SNode* ret = NULL;
		
		for(int i=0;i<N;i++)
		{
			if(!m_used[i])
			{
				ret = reinterpret_cast<SNode*>(m_space) + i;
				ret = new(ret)SNode();//在ret处调用SNode的构造函数
				m_used[i] = 1;
				break;
			}
		}
		return ret;
	}

	virtual	void destroy(Node* pn)
	{
		SNode* ret = reinterpret_cast<SNode*>(m_space);
		SNode* psn = dynamic_cast<SNode*>(pn);

		for(int i = 0;i<N;i++)
		{
			if(pn == ret + i)
			{
				m_used[i] = 0;
				psn->~SNode();//调用SNode的析构函数
				break;
			}
		}
	}
public:
	StaticLinkList()
	{
		for(int i=0;i<N;i++)
		{
			m_used[i] = 0;
		}
	}

	int capacity()
	{
		return N;
	}

	~StaticLinkList()
    {
        this->clear();//调用父类的析构函数
    } 
};


}


#endif

CircleList

  CircleList的设计特点:
    定义内部函数last_to_first() , 用于将单链表首尾相连
    注意事项:首元素的插入操作和删除操作
    重新实现:清空操作和遍历操作

CircleList.h

#ifndef _CircleList_H_
#define _CircleList_H_
#include "include/LinkList.h"

namespace JYlib
{

template < typename T >
class CircleList:public LinkList<T>
{
protected:
    typedef typename LinkList<T>::Node  Node;

	int mod(int i)const
	{
		return (this->m_length == 0 ? 0 : i%(this->m_length));
	}

	//返回尾结点
    Node* last()const
	{
		return this->position(this->m_length);
	}

	//尾结点指向头结点
	void last_to_first()
	{
		last()->next = this->m_header.next;
	}
public:
	bool insert(const T& e)
	{
		return insert(this->m_length,e);
	}

	bool insert(int i,const T& e)
	{
		i = i %(this->m_length + 1);//归一化处理

        bool ret = LinkList<T>::insert(i,e);

		if(ret && (i == 0))
		{
			last_to_first();
		}

		return ret;
	}

	bool remove(int i)
	{
		bool ret = false;
		i = mod(i);

		if(i == 0)//删除的是头结点
		{
            Node* remove = this->m_header.next;

			if(remove == NULL)//没有元素时
			{
				return false;
			}
			else
			{
				this->m_header.next = remove->next;
				this->m_length--;

				if(this->m_length > 0)//除头结点外有结点
				{
					last_to_first();
					
					if(this->m_current == remove)
					{
						this->m_current = this->m_current->next;
					}
				}
				else//除头结点外无结点
				{
					this->m_header.next=NULL;
					this->m_current=NULL;
				}
				this->destroy(remove);
				ret = true;
			}
		}
		else//删除非头结点
		{
			ret = LinkList<T>::remove(i);
		}
		return ret;
	}

	bool set(int i,const T& e)
	{
		return LinkList<T>::set(mod(i),e);
	}

	T get(int i)const
	{
		return LinkList<T>::get(mod(i));
	}

	T get(int i,const T& e)const
	{
		return LinkList<T>::get(mod(i),e);
	}

	int find(const T& e)const
	{
		int ret = -1;

        Node* silder = this->m_header.next;

		for(int i=0;i<this->m_length;i++)
		{
			if(silder->value == e)
			{
				ret = i;
				break;
			}
			silder = silder->next;
		}

		return ret;
	}

	void clear()
	{
		if(this->m_length > 0)
		{
			while(this->m_length > 1)
			{
				remove(1);//remove(0)效率太低
			}
			if(this->m_length == 1)//首结点需要置空
			{
                Node* remove =  this->m_header.next;

				this->m_header.next = NULL;
				this->m_current = NULL;
				this->m_length = 0;

				this->destroy(remove);
			}
		}

	}

	bool move(int i,int step = 1)
	{
		return LinkList<T>::move(mod(i),step);
	}

	bool end()
	{
		return (this->m_length == 0)||(this->m_current == NULL); 
	}

	~CircleList()
	{
		clear();
	}
};

}


#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值