#include
using namespace std;
//注意三种情况:一是空链表;二是只有一个元素;三是两个及以上的元素
template
class IntSLLNode
{
public:
IntSLLNode() { next = NULL; }
IntSLLNode(T el, IntSLLNode *netel=NULL) {
data = el;
next = netel;
}
//可以不需要析构函数,因为系统会自定义一个析构函数将next与data销毁。
//~IntSLLNode() { cout << "被删除" << endl; next = NULL;data = 0; };
T data;
IntSLLNode *next;
};
template
class IntSLLList
{
public:
IntSLLList();
IntSLLList(IntSLLNode *el) { head->next = el->next; };
void CreateLinkList(int n);
void TravalLinkList();
int GetLength();
bool IsEmpty() { return (head->next == NULL || tail->next == NULL) ? true : false; };
void find(T find_data);
void InsertElemAtEnd(T data);
void InsertElemAtFront(T data);
void InsertElemAtIndex(T data, int index);
void DeleteElemAtEnd();
void DeleteElemAtFront();
void DeleteElemAtPoint(T find_data);
void DeleteAll();
void GetElemAtEnd();
void GetElemAtFront();
void bubblesort();
private:
IntSLLNode *head;//仅仅只是储存了一个指向IntSLLNode的地址指针,但此指针并没有指向任何IntSLLNode,因为没有实例化
IntSLLNode *tail;
};
template
IntSLLList::IntSLLList()
{
head=new IntSLLNode(0);//此head为IntSLLList内部定义的head,因此其生命周期是跟随IntSLLList的实例化的。此时head实例化,有具体指向的内容。
//IntSLLNode *head = new IntSLLNode(5);
//代码出错,因为此时属于新定义了一个IntSLLNode变量head,而并非给类中的head地址所指内存赋予IntSLLNode类
//因此其生命周期仅为在此局部函数中,另外注意,head指针的内存会销毁,但其开辟在堆上的IntSLLNode不会销毁,需要delete来销毁。
/*head->data = 0;
head->next = NULL;*/
tail = new IntSLLNode(0);
/*tail->data = 0;
tail->next = NULL;*/
}
template
void IntSLLList::CreateLinkList(int n)
{
T num;
IntSLLNode *ptemp;
ptemp=head;
if (n <= 0) {
cout << "输入有误" << endl;
return;
}
for (int i = 1;i <= n;++i) {
cout << "第" << i << "个元素的值为" << endl;
cin >> num;
//总共三步
//1、在堆中建立一个新节点
//2、将旧的节点指向新节点
//3、旧节点往前移,成为新节点
IntSLLNode *pnew = new IntSLLNode(num);
ptemp->next = pnew;
ptemp = pnew;
}
tail->next = ptemp;
}
template
void IntSLLList::TravalLinkList()
{
IntSLLNode *ptemp;
ptemp = head;
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
cout << "链表遍历为" << endl;
while (ptemp->next != NULL) {
ptemp = ptemp->next;
cout << ptemp->data << " ";
}
cout << endl << "一共有" << GetLength() << "个元素" << endl;
}
template
void IntSLLList::GetElemAtEnd()
{
if (head->next == NULL || tail->next == NULL)
cout << "此链表为空链表" << endl;
else
cout << "末尾元素为" << tail->next->data << endl;
}
template
void IntSLLList::GetElemAtFront()
{
if (head->next == NULL || tail->next == NULL)
cout << "此链表为空链表" << endl;
else
cout << "首位元素为" << head->next->data << endl;
}
template
void IntSLLList::bubblesort()
{
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
IntSLLNode *ptemp, *ptemp2;
int i, j;
int len = GetLength();
//冒泡排序第一层只需要比较n-1次
for (i = 0, ptemp = head->next;i < len - 1;++i)
//第一层需要比较n-1-i次
for (j = 0, ptemp2 = ptemp;j < len - 1 - i;++j, ptemp2 = ptemp2->next)
if (ptemp2->data > ptemp2->next->data)
swap(ptemp2->data, ptemp2->next->data);
cout << "链表已排序好" << endl;
return;
}
template
int IntSLLList::GetLength()
{
IntSLLNode *ptemp;
int i = 0;
ptemp = head;
while (ptemp->next != NULL) {
ptemp = ptemp->next;
i++;
}
return i;
}
template
void IntSLLList::find(T find_data)
//ptemp = head;??
//二者地址一样,因此对head操作即为对ptemp操作
//那在其他函数上都进行了ptemp = ptemp->next;
//亦就是head=head->next????
//跟创建节点的想法有出入!!!
//ptemp->next;和*ptemp->next;有什么区别!!
//ptemp只是一个地址,为啥会有ptemp->next?不应先调用地址上的内存吗?例如*ptemp
/******************************************************************************/
//创建节点那里只是一开始将head的值赋予给ptemp,因此对ptemp操作相当于对head操作
//而后就将pnew的值赋予给ptemp,因此此时对ptemp操作不等于对head操作。
/******************************************************************************/
//->是指针的指向运算符,用法就是这样的
{
IntSLLNode *ptemp;
int i = 0;
bool isAppear = false;
ptemp = head;
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
while (ptemp->next != NULL) {
i++;
ptemp = ptemp->next;
if (ptemp->data == find_data) {
cout << "此元素出现在链表中的第" << i << "个" << endl;
isAppear = true;
}
}
if (!isAppear)
cout << "此元素没有出现过" << endl;
return;
}
template
void IntSLLList::InsertElemAtEnd(T data)
{
IntSLLNode *ptemp=new IntSLLNode(data);
if (head->next == NULL || tail->next == NULL)
head->next = tail->next = ptemp;
else{
tail->next->next = ptemp;
tail->next = ptemp;
}
}
template
void IntSLLList::InsertElemAtFront(T data)
{
IntSLLNode *ptemp = new IntSLLNode(data);
if (head->next == NULL || tail->next == NULL)
head->next = tail->next = ptemp;
else {
ptemp->next = head->next;
head->next = ptemp;
}
}
template
void IntSLLList::InsertElemAtIndex(T data, int index)
{
IntSLLNode *ptemp;
ptemp = head;
if (index > GetLength()) {
cout << "索引大于链表元素个数,将此节点放置链表末端" << endl;
InsertElemAtEnd(data);
}
else if (index <= 0) {
cout << "索引小于等于0,将此节点放置链表最前端" << endl;
InsertElemAtFront(data);
}
else {
while (index != 1) {
ptemp = ptemp->next;
index--;
}
IntSLLNode *pNode = new IntSLLNode(data, ptemp->next);
ptemp->next = pNode;
}
}
//注意三种情况:一是空链表;二是只有一个元素;三是两个及以上的元素
template
void IntSLLList::DeleteElemAtEnd()
{
IntSLLNode *ptemp;
ptemp = head;
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
if (head->next == tail->next) {//此链表只有一个元素
delete head->next;
head->next = tail->next = NULL;//防止ptemp->next变成野指针
return;
}
while (ptemp->next != tail->next) {
ptemp = ptemp->next;
}
delete ptemp->next;//删除了这个ptemp->next指针指向的内容,但这个指针并不会被delete删除
//ptemp->next指向的节点中的next会自动变为NULL吗?有无自定义析构函数的区别?
ptemp->next = NULL;//防止ptemp->next变成野指针
tail->next = ptemp;
}
//注意三种情况:一是空链表;二是只有一个元素;三是两个及以上的元素
template
void IntSLLList::DeleteElemAtFront()//只有一个元素!!
{
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
if (head->next == tail->next) {//此链表只有一个元素
delete head->next;
head->next = tail->next = NULL;//防止ptemp->next变成野指针
return;
}
IntSLLNode *ptemp;
ptemp = head->next->next;
delete head->next;//删除了这个ptemp->next指针指向的内容,但这个指针并不会被delete删除
//ptemp->next指向的节点中的next会自动变为NULL吗?有无自定义析构函数的区别?
//会!这是系统自动生成的析构函数的作用
//但是由于head这个节点并没有被销毁,因此head->next不会赋值为NULL,必须手动赋值。
head->next = ptemp;
}
template
void IntSLLList::DeleteElemAtPoint(T find_data)
//倘若只有一个元素?倘若删除的元素在末尾?在首部?
{
IntSLLNode *ptemp, *ptemp2 = NULL;
bool isAppear = false;
ptemp = head;
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
if (head->next == tail->next && head->next->data == find_data) {//仅有一个元素
isAppear = true;
delete head->next;
head->next = tail->next = NULL;//防止ptemp->next变成野指针
return;
}
while (ptemp->next != NULL) {
if (ptemp->next->data == find_data) {
isAppear = true;
ptemp2 = ptemp->next->next;
if (tail->next == ptemp->next) {//在末尾
tail->next = ptemp;
delete ptemp->next;
ptemp->next = ptemp2;
return;
}
delete ptemp->next;
ptemp->next = ptemp2;
continue;
}
ptemp = ptemp->next;
}
if (!isAppear)
cout << "此元素没有出现过" << endl;
return;
}
template
void IntSLLList::DeleteAll()
{
IntSLLNode *ptemp;
ptemp = head;
if (head->next == NULL || tail->next == NULL) {
cout << "此链表为空链表" << endl;
return;
}
while (head->next != NULL) {
DeleteElemAtFront();
}
}
int main()
{
IntSLLList l;
int n;
int i;
int j;
cout << "1.创建单链表 2.遍历单链表 3.获取单链表的长度 4.判断单链表是否为空 5.获取节点\n";
cout << "6.在尾部插入指定元素 7.在指定位置插入指定元素 8.在头部插入指定元素\n";
cout << "9.在尾部删除元素 10.删除所有元素 11.删除指定元素 12.在头部删除元素 \n";
cout << "13.返回末尾元素 14.返回首位元素 15.将链表元素排正序(冒泡排序)\n";
cout<<"0.退出" << endl;
do {
cout << "请输入要执行的操作: " << endl;
cin >> i;
switch (i)
{
case 1:
cout << "输入创建链表的个数" << endl;
cin >> n;
l.CreateLinkList(n);
break;
case 2:
l.TravalLinkList();
break;
case 3:
cout << "一共有" << l.GetLength() << "个元素" << endl;
break;
case 4:
if (l.IsEmpty())
cout << "此链表为空链表" << endl;
else
cout << "此链表非空链表" << endl;
break;
case 5:
cout << "输入待获取的节点的数值" << endl;
cin >> n;
l.find(n);
break;
case 6:
cout << "输入需要在后方添加的数值" << endl;
cin >> n;
l.InsertElemAtEnd(n);
break;
case 7:
cout << "输入需要在链表何处插入数值" << endl;
cin >> n;
cout << "输入需要插入数值" << endl;
cin >> j;
l.InsertElemAtIndex(j, n);
break;
case 8:
cout << "输入需要在前方添加的数值" << endl;
cin >> n;
l.InsertElemAtFront(n);
break;
case 9:
l.DeleteElemAtEnd();
break;
case 10:
l.DeleteAll();
break;
case 11:
cout << "输入待删除的数值" << endl;
cin >> n;
l.DeleteElemAtPoint(n);
break;
case 12:
l.DeleteElemAtFront();
break;
case 13:
l.GetElemAtEnd();
break;
case 14:
l.GetElemAtFront();
break;
case 15:
l.bubblesort();
break;
}
} while (i != 0);
system("pause");
return 0;
}