这个学期初识数据结构,不知其为何物,更不知道其在软件工程以及计算机科学中的重要地位与作用。在这个学期的第一个月内我只做了一件事,那就是认真的读教材,认真的分析书上的所有代码,慢慢的我也懂了什么是数据结构了,并在看书的过程中不断的思考为什么要这么做,中途也会自己冒出一些想法来。因为教材有不完善或是错误的地方(后续的结构中会在代码中提示出来),而且确实诟病很多,所以我就在看懂一个结构后对他修修改改使结构定义更完善,使函数签名更符合需求等等,由于采用的是通用的模板形式,所以在后面写代码的时候还参考了部分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;
}
}
}
}