一,单链表的基本概念
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。
链表中的每个节点都保存有指向下一个节点的指针,所有节点串成一条链。根据指针的不同,还有单链表、双链表和循环链表的区分,如下图所示。
单链表是只包含指向下一个节点的指针,只能单向遍历。
双链表即包含指向下一个节点的指针,也包含指向前一个节点的指针,因此可以双向遍历。
循环单链表则是将尾节点与首节点链接起来,形成了一个环状结构,在某些情况下会非常有用。
由于链表是使用指针将节点连起来,因此无需使用连续的空间,它具有以下特点:
1)长度不固定,可以任意增删。
2)存储空间不连续,数据元素之间使用指针相连,每个数据元素只能访问周围的一个元素
3)存储密度小,因为每个数据元素,都需要额外存储一个指向下一元素的指针(双链表则需要两个指针)。
4)要访问特定元素,只能从链表头开始,遍历到该元素,时间复杂度为 O(n)。
5)在特定的数据元素之后插入或删除元素,不涉及到其他元素的移动,因此时间复杂度为 O(1)。
template
class LinkNode
{
public:
LinkNode()//无参构造函数
{
next = NULL;
}
//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
LinkNode(DataType item)
{
next = NULL;
data = item;
}
friend class LinkList;//允许链表类任意访问节点类的私有变量
private:
DataType data;//节点数据
LinkNode *next;//节点指针(指向下一个节点)
};
// 带头结点的单链表定义
template
class LinkList
{
public:
//带有链表规模的构造函数
LinkList(int size)
{
head = new LinkNode;//头结点
maxSize=size;//指定最大规模
nLength=0;//链表长度
}
//析构函数
~LinkList()
{
DestroyList();
}
//摧毁链表
void DestroyList();
//获取链表长度
int Length() const
{
return nLength;
}
//定位指定的位置,返回该位置上的结点指针
LinkNode* Locate(int pos);
//在指定位置pos插入值为item的结点,失败返回false
bool Insert(DataType item, int pos);
//删除指定位置pos上的结点,item就是该结点的值,失败返回false
bool Remove(int pos);
//获取指定位置pos的结点的值,失败返回false
DataType GetData(int pos);
//更改指定位置pos的结点的值,失败返回false
bool ChangeData(int pos, DataType item);
//判断链表是否为空
bool IsEmpty() const;
//打印链表
void Print() const;
//链表排序
void SortList();
//链表逆置
void Reverse();
//创建一个链表环
void CreatCircle();
//关闭单链表环
void KillCircle();
//判断是否纯在单链表环,方法一
bool IsCircle1();
//判断是否纯在单链表环,方法二
bool IsCircle2();
private:
LinkNode *head;//头结点
int maxSize;//允许链表最大规模
int nLength;//聊表长度
};
#include "stdafx.h"
/* 单链表的结点定义 */
#include "iostream"
using namespace std;
templateclass LinkList;
template
class LinkNode
{
public:
LinkNode()
{
next = NULL;
}
//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
LinkNode(DataType item)
{
next = NULL;
data = item;
}
friend class LinkList;
private:
DataType data;
LinkNode *next;
};
/* 返回链表中第pos个元素的地址,如果pos<0或pos超出链表最大个数返回NULL */
template
LinkNode* LinkList::Locate(int pos)
{
LinkNode *p = head;//head和p指向共同的内容,头结点无数据,只是个指针
if (pos < 0)
{
cerr<<"位置参数有错误"<next;
i++;
}
return p;
}
template
bool LinkList::Insert(DataType item, int pos)
{
if (Length() >= maxSize)
{
cout<<"错误:链表已满"< *p = Locate(pos);
if (NULL == p)
return false;
LinkNode *newNode = new LinkNode(item);//创建新节点
if (NULL == newNode)
{
cerr << "分配内存失败!" << endl;
exit(1);
}
newNode->next = p->next;
p->next = newNode;
nLength++;
return true;
}
template
bool LinkList::Remove(int pos)
{
LinkNode *p = Locate(pos);
if (NULL == p || NULL == p->next)
return false;
LinkNode *del = p->next;
p->next = del->next;
delete del;
nLength--;
return true;
}
template
void LinkList::DestroyList()
{
LinkNode *p = NULL;
//遍历链表,每次都删除头结点的next结点,最后保留头结点
while (NULL != head->next)
{
p = head->next;
head->next = p->next; //每次都删除头结点的next结点
delete p;
}
}
template
void LinkList::Print() const
{
int count = 0;
LinkNode *p = head;
while (NULL != p->next)
{
p = p->next;
std::cout << p->data << " ";
if (++count % 15 == 0) //每隔十个元素,换行打印
cout << std::endl;
}
}
template
void LinkList::Reverse()
{
LinkNode *preNode = head->next;
LinkNode *curNode = preNode->next;
LinkNode *next = NULL;
head->next->next = NULL;
while (curNode)
{
next = curNode->next;
curNode->next = preNode;
preNode = curNode;
curNode = next;
}
head->next = preNode;
}
//判断链表是否为空
template
bool LinkList::IsEmpty() const
{
if (Length()==0)
{
return true;
}
else
{
return false;
}
}
//更改指定位置pos的结点的值,失败返回false
template
bool LinkList::ChangeData(int pos, DataType item)
{
LinkNode *p=Locate(pos);
if (pos < 0||pos>=Length())
{
cout<<"位置参数有错误"<data=item;
return true;
}
template
DataType LinkList::GetData(int pos)
{
LinkNode *p=Locate(pos);
return p->data;
}
//链表排序
template
void LinkList:: SortList()
{
for (int i=1;i *curNode=Locate(i);
for (int j=i+1;j *afterNode=Locate(j);
if (afterNode->data>curNode->data)
{
DataType temp;
temp=curNode->data;
curNode->data=afterNode->data;
afterNode->data=temp;
}
}
}
}
//创建一个链表环
template
void LinkList:: CreatCircle()
{
int nLen=Length();
int nLen1=nLen/2-1;
LinkNode *ptail=Locate(nLen-1);
LinkNode *pcirStart=Locate(nLen1);
ptail->next=pcirStart;
}
//关闭链表环
template
void LinkList:: KillCircle()
{
int nLen=Length();
LinkNode *ptail=Locate(nLen-1);
ptail->next=NULL;
}
//是否纯在链表环?方法1
template
bool LinkList::IsCircle1()
{
int nLen=Length();
LinkNode *ptail=Locate(nLen-1);
if (ptail->next!=NULL)
{
return true;
}
return false;
}
//是否纯在链表环?方法2
template
bool LinkList::IsCircle2()
{
if ( head ==NULL)
{
cerr<<"空链表"< *pFast,*pSlow;
pSlow=head;
pFast=head;
while(pFast!=NULL&&pFast->next!=NULL)
{
pFast=pFast->next->next;
pSlow=pSlow->next;
if (pSlow==pFast)
{
return true;
break;
}
}
return false;
}
// ConsoleAppLinklist.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "iostream"
#include "LinkList.h"
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
LinkList A(10);
char srcStr='A';
for (int i=0;i<10;i++)
{
A.Insert(srcStr++,i);//i=0,产生第一个节点,而不是头结点
}
cout<<"初始化的链表为:"<>nPos;
bool flag=false;
do
{
if (nPos>=A.Length())
{
cerr<<"参数过大,请重新输入"<>nPos;
flag=true;
}else
{
flag=false;
}
} while (flag);
A.Remove(nPos);
cout<<"已删除,是否继续执行该操作(y/n)?"<>ans;
} while (ans=='y'||ans=='Y');
cout<<"删除后的链表为:"<>nPos1>>data;
A.ChangeData(nPos1,data);
A.Print();
cout<
输入一个链表,输出该链表中倒数第k个结点。(hint: 请务必使用链表。)
输入可能包含多个测试样例,输入以EOF结束。对于每个测试案例,输入的第一行为两个整数n和k(0<=n<=1000, 0<=k<=1000):n代表将要输入的链表元素的个数,k代表要查询倒数第几个的元素。输入的第二行包括n个数t(1<=t<=1000000):代表链表中的元素。
对应每个测试案例,若有结果,输出相应的查找结果。否则,输出NULL。
5 2 1 2 3 4 5 1 0 5
4 NULL
#include "vector"
#include "string"
#include "algorithm"
#include
#include "stack"
#include
using namespace std;
//节点定义
class LinkNode
{
public:
LinkNode(int item)//有参数的构造
{//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
next = NULL;
data = item;
}
friend class LinkList;//允许链表类随意访问节点数据
private:
int data;
LinkNode *next;
};
// 带头结点的单链表定义
class LinkList
{
public:
LinkList()
{
head = new LinkNode(0);//头结点,并未该节点赋值0
nLength = 0;
}
~LinkList(){}
//定位指定的位置,返回该位置上的结点指针
LinkNode* Locate(int pos);
//在指定位置pos插入值为item的结点,失败返回false
bool Insert(int item, int pos);
//打印倒数第k个链表节点
void Print(int k);
private:
LinkNode *head;//头结点指针
int nLength;//统计节点长度,不算头结点
};
//返回链表中第pos个元素的地址,第0个元素是头结点
LinkNode* LinkList::Locate(int pos)
{
LinkNode *p = head;
int i = 0;
while (p != NULL && i < pos)//p==NULL说明是末尾了
{
p = p->next;
i++;
}
return p;
}
//在pos位置的节点后面插入新节点并赋值item
bool LinkList::Insert(int item, int pos)
{
LinkNode *p = Locate(pos);
LinkNode *newNode = new LinkNode(item);//创建新节点,该节点值为item
//建立连接,注意新节点是插在pos位置的后面
newNode->next = p->next;
p->next = newNode;
nLength++;
return true;
}
void LinkList::Print(int k)
{
LinkNode *p = Locate(this->nLength - k + 1);//第零个节点是头结点,不用输出
cout << p->data << endl;
}
int main()
{
int n = 0, k = 0, val = 0;
while (cin>>n>>k)
{
if (n > 0)
{
LinkList list;
for (int i = 0; i < n; i++)
{
cin >> val;
list.Insert(val, i);//在第i个位置之后插入值为val的节点
}
if (k == 0 || k > n)
{
cout << "NULL" << endl;
continue;
}
list.Print(k);
}
else
{
cout << "NULL" << endl;
continue;
}
}
return 0;
}
输入一个链表,反转链表后,输出链表的所有元素。
(hint : 请务必使用链表)
输入可能包含多个测试样例,输入以EOF结束。
对于每个测试案例,输入的第一行为一个整数n(0<=n<=1000):代表将要输入的链表的个数。
输入的第二行包含n个整数t(0<=t<=1000000):代表链表元素。
对应每个测试案例,
以此输出链表反转后的元素,如没有元素则输出NULL。
5 1 2 3 4 5 0
5 4 3 2 1 NULL
#include "iostream"
using namespace std;
//节点定义
class LinkNode
{
public:
LinkNode(int item)//有参数的构造
{//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
next = NULL;
data = item;
}
friend class LinkList;//允许链表类随意访问节点数据
private:
int data;
LinkNode *next;
};
// 带头结点的单链表定义
class LinkList
{
public:
//有参数的构造函数
LinkList(int size)
{
head = new LinkNode(0);//头结点,并未该节点赋值0
nLength = 0;
}
~LinkList(){}
//定位指定的位置,返回该位置上的结点指针
LinkNode* Locate(int pos);
//在指定位置pos插入值为item的结点,失败返回false
bool Insert(int item, int pos);
//反转链表
void Reverse(int len);
private:
LinkNode *head;//头结点指针
int nLength;//统计节点长度,不算头结点
};
//返回链表中第pos个元素的地址,第0个元素是头结点
LinkNode* LinkList::Locate(int pos)
{
LinkNode *p = head;
int i = 0;
while (p != NULL && i < pos)//p==NULL说明是末尾了
{
p = p->next;
i++;
}
return p;
}
//在pos位置的节点后面插入新节点并赋值item
bool LinkList::Insert(int item, int pos)
{
LinkNode *p = Locate(pos);
LinkNode *newNode = new LinkNode(item);//创建新节点,该节点值为item
//建立连接
newNode->next = p->next;
p->next = newNode;
nLength++;
return true;
}
void LinkList::Reverse(int len)
{
for (size_t i = 0; i < len; i++)
{
LinkNode *p = Locate(len-i);
if (i==0)
cout << p->data;
else
cout <<" "<< p->data;
}
cout << endl;
}
int main()
{
int n = 0, val = 0;
while (cin>>n)
{
if (n == 0)
{
cout << "NULL" << endl;
continue;
}
LinkList s(n);
for (int i = 0; i < n; i++)
{
cin >> val;
s.Insert(val, i);//在第i个位置插入值为val的节点
}
s.Reverse(n);
}
return 0;
}
【1】《算法导论》
【2】《维基百科》
【3】http://www.cnblogs.com/scandy-yuan/archive/2013/01/06/2847801.html
【4】http://www.cppblog.com/cxiaojia/archive/2012/07/31/185760.html
【5】《STL源码剥析》,侯捷著
【6】《C++妙趣横生的算法》,胡浩著
【7】九度OJ,http://ac.jobdu.com/problemset.php?search=二叉树
注:
本文部分文字学习并copy自网络,代码参考并改编自《算法导论》.
如果侵犯了您的版权,请联系本人[email protected],本人将及时编辑掉!