单链表的概念和特点
单链表是一种最简单的链表表示,用它来表示线性表时,用指针表示结点间的逻辑关系。因此单链表的一个存储结点(node)包含两个部分:data域和link域。
data域称为数据域,用于存储线性表的一个数据元素。link域称为指针域或链域,用于存放一个指针,该指针指示该链表中下一个结点的开始存储地址。
单链表的特点时长度可以很方便地进行扩充。当链表要增加一个新的结点时,只要可用存储空间允许,就可以为链表分配一个结点空间,供链表使用。因此,线性表中数据元素的顺序与其链表表示的结点的物理顺序可能不一致,一般通过单链表的指针将各个数据元素按照线性表的逻辑顺序链接起来。所以,当进行插入或删除运算时,只需修改相关结点的指针域即可,既方便又省时。但是,由于链表的每个结点导游指针域,因而在存储空间上比顺存储要#付出更大的代价。
类的定义与实现
单链表包括链表的结点(linknode)类和链表(list)类。
通常,用struct定义LinkNode类,并在List类中将其封装在内部,因此属于该List的所有LinkNode实例都成为List实例的私有成员,从而保证不被外界直接访问。
#pragma once
#include<iostream>
#include<stdlib.h>
using namespace std;
const int maxSize=10;
template<class T>
struct LinkNode
{
T data; //数据域
LinkNode<T>* link; //结点域
LinkNode(LinkNode<T>* ptr = NULL)
{
link = ptr;
}
LinkNode(const T& item,LinkNode<T>* ptr = NULL)
{
data = item;
link = ptr;
}
};
template<class T>
class List
{
public:
List() //构造函数
{
last= first = new LinkNode<T>;
}
List(const T& x) //重载构造函数
{
first = new LinkNode<T>(x);
}
List(List<T>& L); //复制构造函数
~List() //析构函数
{
makeEmpty();
}
void makeEmpty(); //置空
int Length()const; //长度
LinkNode<T>* getHead()const {
return first; } //头节点
LinkNode<T>* getTail()const {
return last;} //尾节点
LinkNode<T>* getFirst()const {
return first->link;} //第一个数据节点
LinkNode<T>* Search(T x); // 查找
LinkNode<T>* Locate(int i); //定位
bool getData(int i, T& x); //取值 (第i个位置)
bool getData(LinkNode<T> *p,T &x); //取值 (指针p对应的数据)
bool setData(int i, T& x); //设置值
bool Insert(int i, T& x); //插入
bool Remove(int i, T& x); //删除
bool IsEmpty()const //判空
{
return first->link == NULL ? true : false;
}
bool IsFull()const {
return false; } //判满
void Sort(); //排序(改进的冒泡排序)
void input(T engTag); //前插法输入
void inputBack(T engTag); //后插法输入
void output(); //输出
int Size() {
cout << "Uncertain!" << endl; return 1; }//尺寸
void Link(List<T> &L); //连接
void merge(List<T>& LA,List<T>& LB); //归并
void doubleRemove(List<T>& L); //删除相同数据的第一处前一节点
List<T>& split(List<T>& L); //拆分(奇偶)
List<T>& operator=(List<T>& L); //赋值
protected:
LinkNode<T>* first,* last; //头节点 尾节点
};
为了实现的方便,为每一个链表加上一个“附加头结点”,位于链表第一个结点之前。附加头结点的data域不存储任何信息,也可以存放一个特殊标志或表长。
采用附加头结点,统一了空表和非空表操作的实现,降低了程序结构上的复杂性,减少了出错的概率。
template <class T>
List<T>::List(List<T>& L) //复制构造函数
{
T value;
LinkNode<T>* srcptr = L.getFirst();
LinkNode<T>* desptr = last = first = new LinkNode<T>;//创建新表
while (srcptr != NULL)
{
value = srcptr->data;
desptr->link = new LinkNode<T>(value);//创建新数据节点
desptr = desptr->link; //放入新表
srcptr = srcptr->link;
last=last->link;
}
desptr->link = NULL;
}
template<class T>
void List<T>::makeEmpty() //置空
{
LinkNode<T>* q;
while (first->link != NULL) //反复删除,直至空
{
q = first->link;
q->link = first->link; //使当前节点脱离链表
delete q; //删除当前节点
}
}
template<class T>
int List<T>::Length()const //数据长度
{
LinkNode<T>* p;
p = first->link;
int count = 0; //计数器
while (p != NULL)
{
p = p->link; //有数据
count++; //递增
}
return count;
}
template<class T>
LinkNode<T>* List<T>::Search(T x) //查找
{
LinkNode<T>* current = first->link; //从第一个数据开始查找
while (current != NULL)
{
if (current->data == x)
break;
else
current = current->link; //未找到则指针沿链表后移
}
return current;
}
template <class T>
LinkNode<T>* List<T>::Locate(int i) //定位
{
if (i < 0)
return NULL;
LinkNode<T>* current = first->link;
int k = 0; //位置标记
while (current != NULL && k < i)
{
current = current ->link;
k++;
}
return current;
}
template <class T>
bool List<T>::getData(int i, T& x) //取值
{
if (i <= 0)
return false;
LinkNode<T>* current = Locate(i); //定位
if (current == NULL)
return false;
else
{
x = current->data; //值赋给x
return true;
}
}
template<class T>
bool List<T>::getData(LinkNode<T> *p,T &x) //取值
{
LinkNode<T>* q=first;
int flag=0;
while (q!=last)
{
if (q==p) //指针相同
{
x=q->data; //值赋给x
flag=1;
}
}
return flag;
}
template<class T>
bool List<T>::setData(int i, T& x) //设置值
{
if (i <= 0)
return false;
LinkNode<T>* current = Locate(i); //定位
if (current == NULL)
return false;
else
{
current->data = x;
return true;
}
}
template <class T>
bool List<T>::Insert(int i, T& x) //插入
{
if (i <= 0)
return false;
LinkNode<T>* current = Locate(i-1); //定位到前一个结点
if (current == NULL)
return false;
LinkNode<T>* newNode = new LinkNode<T>(x); //生成新结点
if (newNode == NULL)
return false;
newNode->link = current->link; //新节点和后件连接
current->link = newNode; //新节点和前驱连接
return true;
}
template <class T>
bool List<T>::Remove(int i, T& x) //删除
{
if (i <= 0)
return false;
LinkNode<T>* current = Locate(i-1); //定位
if (current == NULL || current->link==NULL)
return false;
LinkNode<T>* del = current->link; //置一指针给待删结点
current->link = del->link; //删除关系
x = del->data; //删除节点数据赋给x
delete del; //删除节点
return true;
}
template<class T>
void List<T>::output() //输出
{
LinkNode<T>* current = first->link;
while (current != NULL)
{
cout << current->data << "->";
current = current->link; //当前节点沿链表后移
}
cout << endl;
}
template<class T>
List<T>& List<T>::operator=(List<T>& L) //赋值
{
T value;
LinkNode<T>* srcptr = L.getFirst();
LinkNode<T>* desptr = new LinkNode<T>; //创建新表
while (srcptr->link != NULL)
{
value = srcptr->data;
desptr->link = new LinkNode<T>(value); //产生新数据节点
srcptr = srcptr->link;
desptr = desptr->link;
}
desptr->link = NULL;
return *this;
}
template<class T>
void List<T>::input(T endTag) //前插法
{
cout<<"前插法"<<endl;
cout << "Constructing Linkedlist, pleaes enter in the number(Press 0 to stop):" << endl;
LinkNode<T>* newNode;
T val; //设置停止输入记号
makeEmpty();
last=first;
cin >> val;
int i=0;
while (val != endTag)
{
newNode = new LinkNode<T>(val); //产生新数据节点
if (newNode == NULL)
cout << "Error:Distribution Failor!" << endl;
newNode->link = first -> link; //与第一个数据节点结点连接
first->link = newNode; // 与头结点连接
if(i<1)
{
last=first->link; //特殊情况下尾节点处理
i++;
}
cin >> val;
}
}
template<class T>
void List<T>::inputBack(T endTag) //后插法
{
cout<<"后插法"<<endl;
LinkNode<T>* newNode;
T val; //设置输入停止记号
makeEmpty();
cin >> val;
last = first;
while (val != endTag)
{
newNode = new LinkNode<T>(val); //产生新的数据节点
if (newNode == NULL)
cout<<"Distribution Failor!"<<endl;
last->link = newNode; //与原尾节点连接
last = newNode; //更新尾节点
cin >> val;
}
last->link = NULL;
}
template <class T>
void List<T>::Link(List<T> &L) //连接
{
LinkNode<T>* newhead=L.getHead();
last->link=newhead->link; //前表的尾节点与后表的头指针连接
last=L.getTail(); //更新尾节点
}
template<class T>
void List<T>::Sort() //冒泡排序
{
int flag=0;
while (last!=first)
{
flag=0; //循环跳出标记
LinkNode<T>* current=first->link; //从第一个数据节点开始
while (current!= last)
{
if (current->data > current->link->data)
//若前一个结点数据大于后一个结点数据
{
T temp=current->data;
current->data=current->link->data; //则交换数据
current->link->data=temp;
flag=1;
}
current=current->link;
}
if(flag==0)
//若为进行交换,则已经有序,跳出循环
break;
}
}
template <class T>
void List<T>::merge(List<T>& LA,List<T>& LB)
//归并 (参数两表有序)
{
LA.Sort(), LB.Sort(); //将输入两表排序
LinkNode<T>* current=first;
LinkNode<T>* ptra=LA.getFirst();
LinkNode<T>* ptrb=LB.getFirst();
while (ptra && ptrb)
{
T x,y;
x=ptra->data;
y=ptrb->data; //获取两表结点数据
if (x<y) //取二者较小者放入新表
{
current->link=new LinkNode<T>(x);
//产生新的数据节点
ptra=ptra->link;
//若被取,则指针后移
current=current->link;
//新表当前指针后移
}
else
if(x>y)
{
current->link=new LinkNode<T>(y);
ptrb=ptrb->link;
current=current->link;
}
else
//若二者相等,则任取其一
{
current->link=new LinkNode<T>(x);
ptra=ptra->link;
ptrb=ptrb->link;
//两表指针均后移
current=current->link;
}
}
while (ptra)
//当一表为空,另一表为空,则直接接在新表之后
{
current->link=new LinkNode<T>(ptra->data);
ptra=ptra->link;
current=current->link;
last=ptra; //更新尾节点
}
while (ptrb)
{
current->link=new LinkNode<T>(ptrb->data);
ptrb=ptrb->link;
current=current->link;
last=ptrb;
}
}
template<class T>
void List<T>::doubleRemove(List<T>& L)
//删除相同数据的第一处前一节点
{
L.Sort(); //副本排序
T *Same=new T [maxSize];
for (int i=0;i<maxSize;i++)
{
Same[i]=0; //初始化数组(存放所有相同元素)
}
int i=0;
LinkNode<T>* current=L.getFirst();
while (current->link)
{
if(current->data==current->link->data)
//副本中,若连续数据相同,则放入数组
{
Same[i]=current->data;
i++;
}
current=current->link;
}
for(i=0;Same[i]!=0;i++)
//队所有重复数据循环
{
int flag=1; //循环跳出标记
LinkNode<T>* Cur=getHead(); //当前节点
LinkNode<T>* del=Cur->link; //待删结点
while (del->link && flag)
{
if (del->link->data==Same[i])
//若待删结点的下一节点数据为重复数据,则符合删除要求
{
Cur->link=del->link; //删除关系
delete del; //删除节点
flag=0; //跳至下一个重复元素
}
Cur=Cur->link;
del=Cur->link;
}
}
}
template<class T>
List<T>& List<T>::split(List<T>& L)
{
int flag;
LinkNode<T>* current=L.getHead();
LinkNode<T>* srcptr =current->link; //原表表头
LinkNode<T>* desptr = new LinkNode<T>; //新表表头
first=desptr;
while (srcptr)
{
flag=0;
if (srcptr->data%2!=0) //若原表元素为偶数
{
T value=srcptr->data;
desptr->link = new LinkNode<T>(value); //产生新的数据结点
current->link=srcptr->link; //将此结点从原表剥除
desptr=desptr->link; //加入新表
delete srcptr; //删除原表结点
flag=1;
}
if (!flag)
current=current->link;
if (!current)
break;
srcptr=current->link;
}
desptr->link=NULL;
return *this; //返回产生的新表
//删除后的原表赋给L
}
以下是测试函数。
在main程序中需要链接头文件"LinkedList.h"。
#include <iostream>
#include"LinkedList.h"
/* run this program using the console pauser or add your own getch, system("pause") or input loop */
int main(int argc, char** argv)
{
// 插入测试
/*
int x=2;
List<int> a;
a.Insert(1,x);
a.output();
*/
// 后插法、复制构造函数测试
/*
List<int> a;
cout<<"Input a"<<endl;
a.inputBack(0);
cout << endl;
List<int> b(a);
b.output();
*/
// 前插法、 排序测试
/*
List<T> a,b;
a.input(0);
b.input(0);
cout<<"sorting..."<<endl;
a.Sort();
b.Sort();
a.output();
b.output();
*/
// 归并测试
// List<int> a,b;
// a.input(0);
// b.input(0);
// cout<<"merging..."<<endl;
// a.Sort();
// b.Sort();
// c.merge(a,b);
// c.output();
// 重复元素删前结点测试
List<int> a,b;
a.inputBack(0);
a.output();
b.inputBack(0);
b.output();
a=a.split(b);
a.output();
b.output();
// List<int> b(a);
// b.Sort();
// a.doubleRemove(b);
// a.output();
char stop;
cout << "Press '|' to quit!" << endl;
cin >> stop;
if (stop == '|')
exit(1);
return 0;
}