基本单链表实现

这个学期初识数据结构,不知其为何物,更不知道其在软件工程以及计算机科学中的重要地位与作用。在这个学期的第一个月内我只做了一件事,那就是认真的读教材,认真的分析书上的所有代码,慢慢的我也懂了什么是数据结构了,并在看书的过程中不断的思考为什么要这么做,中途也会自己冒出一些想法来。因为教材有不完善或是错误的地方(后续的结构中会在代码中提示出来),而且确实诟病很多,所以我就在看懂一个结构后对他修修改改使结构定义更完善,使函数签名更符合需求等等,由于采用的是通用的模板形式,所以在后面写代码的时候还参考了部分STL对模板的处理(当然我现在还没能力看懂源码,就连第一章的空间适配器都不懂呢)。合上书自己独立敲代码,起初很困难,总是要翻书,这就表现出你对代码的来龙去脉不熟悉,还须继续看书。不断的迭代中,在指针的指来指去的迷惑中摸爬滚打中,我半独立的敲出了我的第一个数据结构,那就是单链表了。在后面的学习中我保持这种先看书再合上书敲代码的习惯,后面已经能够灵活自如了,看懂一个代码后能很快的敲出来了。现在正值期末复习月,有时间把代码整理一下,陆续贴出。

数据结构一般分为线性结构,层序结构,图结构三种,接下来有很长的篇幅都是有关线性关系的。

每一个链式存储结构都需要有一个结点类型的结构体,由于统一起见笔者在这里都用的是class,并将成员全部置为public。

#pragma once
#include<iostream>
using namespace std;
template<class T>
class LinkNode //一个结构体对外全部可见
{
public:
	T data; //模板类型的一个数据
	LinkNode<T> *link; //该struct的一个指针,用于指向下一个结点
public:
	LinkNode(LinkNode<T>* ptr = NULL)//只初始化指针的构造函数,并使指针域为空
	{ link=ptr;} 
	LinkNode(T& item,LinkNode<T>* ptr = NULL)//数据与指针一同初始化的构造函数,依旧指针域默认参数为空
	{ data=item; link=ptr;}
	~LinkNode(){}
};
单链表的类定义:

#pragma once
#include"LinkNode.h"
#include<iostream>
using namespace std;
template<class T>//继承的过程中,加上此句派生类依然为类模板
class List
{
protected:
	LinkNode<T> *first;//封装
public:
	List();
	List(const T& x);
	List(List<T>& L);
	~List();
	void makeEmpty();//依次销毁每个结点的空间

	int Length() const;//搜索当前表节点数 
	LinkNode<T>* getHead() const;//返回头指针
    LinkNode<T>* Search(T x)const;//搜索并返回指针
	LinkNode<T>* Locate(int i)const;//定位并返回指针

	bool GetData(int i,T& x) const;//返回第i个结点的元素值以引用的形式
	void SetData(int i,T& x);//修改第i个结点的元素值
	bool Insert(int i,T x);//在第i个节点后面插入新节点元素值
	bool Remove(int i,T& x);//删除第i个结点

	bool isEmpty()const;//判表空
	bool isFull()const;//判表满

	void input(T endTag);//创建新链表
	void Sort(); //排序
	//void input();//建立表并输入
	void output();//输出表
	List<T>& operator = (List<T>& L);//复制重载
	
};
由于使用的是模板所以就把实现也放在了头文件中:

template<class T>
List<T>::List()
{
	first=new LinkNode<T>;//调用LinkNode的无参构造函数,初始化指针域为NULL
}
template<class T>
List<T>::List(const T& x)
{
	first=new LinkNode<T>(x) ;//调用LinkNode的无参构造函数,初始化指针域为NULL
}

//深复制构造函数
template<class T>
List<T>::List(List<T>& L)
{
	T value;
	LinkNode<T>* srcptr = L.getHead();//复制一份头指针
	LinkNode<T>* destptr = first = new LinkNode<T>;//调用LinkNode类的无参构造函数,初始化指针域为空,在之后的添加中会把空指针冲掉
	while(srcptr->link != NULL)
	{
		value = srcptr->link->data ;//返回当前指针指向的元素值
		destptr->link = new LinkNode<T>(value) ;//以此值去new出一个新的结点,并插入在新表的最后面
		srcptr = srcptr->link;//当前指针后移
		destptr = destptr->link;
	}
}

template<class T>
List<T>::~List()
{
	makeEmpty();
}
//依次销毁每个结点的空间
template<class T>
void List<T>::makeEmpty()
{
	LinkNode<T> *del;
	while(first->link != NULL)//如果为空表则头结点的指针域为NULL不会执行循环
	{
		del = first->link ;//不为空表第一次循环的情况下,把第1个结点的指针赋给了del指针
		first->link = del->link ;//把当前要删除的结点的后面一个节点接在头结点指针域上
		delete del ;
	}
}
//求表当前节点数
template<class T>
int List<T>::Length() const
{
	LinkNode<T>* current = first ;
	int elemnum=0;
	while(current->link != NULL)//如果头结点指针域为NULL则不执行循环直接返回0值
	{
		current = current->link ;
		elemnum++;
	}
	return elemnum;
}
//返回头指针
template<class T>
LinkNode<T>* List<T>::getHead() const
{
	return first ;
}
//搜索并返回指针
template<class T>
LinkNode<T>* List<T>::Search(T x) const
{
	LinkNode<T>* current = first->link ;//current = first 循环的判断写成 current->link != NULL ,是等价的
	while(current != NULL)
	{
		if(current->data == x)
			break;
		else
			current = current->link ;
	}
	return current ;
}
//定位并返回指针
template<class T>
LinkNode<T>* List<T>::Locate(int i)const//定位可能I为0
{
	if(i<0)
		return NULL;
	LinkNode<T>* current = first;int k=0;
	while(current != NULL && k<i)//起主要控制作用的是k<i ,因为在定位的过程中一般不会出现断点
	{
		current = current->link ;
		k++ ;
	}
	return current ;
}
//以引用的形式返回第i个节点的元素值
template<class T>
bool List<T>::GetData(int i,T& x)const
{
	if(i<=0)//参数必须至少从第1个开始
		return false ;
	LinkNode<T>* current = Locate(i);
	if(current == NULL)//此句一般不会执行
		return false ;
	x = current->data ;
	return true;
}
//修改第i个节点的元素值
template<class T>
void List<T>::SetData(int i,T& x)
{
	if(i<=0)//也是至少从第1个开始
		return ;
	LinkNode<T>* current = Locate(i);
	if(current == NULL)
		return ;
	current->data = x;
}
//在第i个节点后面插入新节点
template<class T>
bool List<T>::Insert(int i,T x)
{
	LinkNode<T> *current = Locate(i);
	if(current == NULL)
		return false;
	LinkNode<T> *newNode = new LinkNode<T>(x);//调用LinkNode的某个构造函数,指针域仍未NULL,但在后面搭链的时候会被覆盖掉
	if(newNode == NULL)
		return false ;
	newNode->link = current->link;//重新搭链
	current->link = newNode;
	return true;//不出现任何意外走到此步骤
}
//删除第i各节点
template<class T>
bool List<T>::Remove(int i,T& x)
{
	LinkNode<T> current = Locate(i-1);//与插入不同的是,删除的是当前第i个节点,为了重现搭链必须还要知道待删除的前一个节点的指针,所以定位到(i-1)
	if((current == NULL)||current->link == NULL)
		return false;
	LinkNode<T>* del = current->link;//此刻的del即为要删除的节点指针
	current->link = del->link;//重新搭链
	x = del->data;//返回节点元素值
	delete del;
	return true;
}
//判表空
template<class T>
bool List<T>::isEmpty()const
{
	return ((first->link == NULL) ? true : false);//每个表都有一个头结点充当数组的第0个元素
}
template<class T>
bool List<T>::isFull()const
{
	//链式表的优势就是可以忽视空间的限制,所以可以不用判表满函数,可以认为链表无满表
}
//后插式创建新链表(存储于逻辑顺序一致)
template<class T>
void List<T>::input(T endTag)
{
	LinkNode<T>* newNode;
	LinkNode<T>* last;//尾结点指针标志
	last = first;//后插式
	T value;
	//makeEmpty();//销毁表
	cin>>value;
	while(value != endTag)//输入停止标志
	{
		newNode = new LinkNode<T>(value);
		if(newNode == NULL)
		{
			cout<<"内存分配错误"<<endl;
			exit(1);
		}
		last->link = newNode;
		last = newNode;

		cin>>value;
	}
}

//输出表
template<class T>
void List<T>::output()
{
	LinkNode<T>* current = first->link;
	while(current != NULL)//知道碰到最后一个指针域为空(后面无节点了)跳出循环,此时已把所有的节点元素值输出完毕
	{
		cout<<current->data<<endl;
		current = current->link;
	}
}

//最小选择排序
template<class T>
void List<T>::Sort()
{
	LinkNode<T> *current1,*current2 ;
	for(current1=first->link;current1!=NULL;current1=current1->link)
	{
		for(current2=current1->link;current2!=NULL;current2=current2->link)
		{
			if(current1->data > current2->data)
			{
				T temp;
				temp=current1->data;
				current1->data=current2->data;
				current2->data=temp;
			}
		}
	}
}



  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值