目录
提示:以下是本篇文章正文内容,下面案例可供参考
一、闵版(char类型)
1.完整代码
#include <iostream>
#include <stdio.h>
using namespace std;
typedef struct DoubleLinkedNode
{
char data;
struct DoubleLinkedNode *previous;//前指针
struct DoubleLinkedNode *next; //后指针
}DLNode,*DLNodePtr;
/*初始化*/
DLNodePtr initLinkList()
{
DLNodePtr tempHeader = new DLNode;
tempHeader->data = '\0';
tempHeader->previous = NULL;
tempHeader->next = NULL;
return tempHeader;
}
/*指定位置插入*/
void insertElement(DLNodePtr paraHeader,char paraChar,int paraPosition)//找到目标位置的前一个结点,插其右边就是
{
DLNodePtr p,q,r;
// Step 1. Search to the position.
p = paraHeader;
for(int i = 0;i<paraPosition;i++)
{
p = p->next;
if(p == NULL)
{
printf("The position %d is beyond the scope of the list.", paraPosition);
return;
}
}
// Step 2. Construct a new node.
q = new DLNode;
q->data = paraChar;
// Step 3. Now link.
r = p->next;//r先指向目标位置结点,因为这里的p->next有双重含义
//1.上一个结点的指向 2.上一个结点的指向的结点地址
q->next = p->next;//新结点的next指向目标位置结点
q->previous = p; //新结点的previous指向原来结点的上一个结点
p->next = q; //修改原来结点的上一个结点的指向
if(r)
r->previous = q;//目标位置结点的next指针不动,修改目标位置结点的previous指向
}
/*按值删除*/
void deleteElementByData(DLNodePtr paraHeader, char paraChar)
{
DLNodePtr q,p,r;
p = paraHeader;
// Step 1. Locate.
while((p->next != NULL)&&(p->next->data != paraChar))
{
p = p->next;
}
// Step 2. Error check.
if (p->next == NULL) {
printf("The char '%c' does not exist.\r\n", paraChar);
return;
}
// Step 3. Change links.
q = p->next;//指向目标结点地址,准备删除
r = q->next;//r指向目标结点的下一个结点
p->next = r;//修改目标结点上一个结点的指向
// Step 4. Free the space.
if(r)
r->previous = p;
free(q);
}
/*打印双向链表*/
void printList(DLNodePtr paraHeader)
{
DLNodePtr p = paraHeader->next;
while(p)
{
cout<<p->data<<" ";
p = p->next;
}
cout<<endl;
}
void insertDeleteTest()
{
// Step 1. Initialize an empty list.
DLNodePtr tempList = initLinkList();
printList(tempList);
// Step 2. Add some characters.
insertElement(tempList, 'H', 0);
insertElement(tempList, 'e', 1);
insertElement(tempList, 'l', 2);
insertElement(tempList, 'l', 3);
insertElement(tempList, 'o', 4);
insertElement(tempList, '!', 5);
printList(tempList);
// Step 3. Delete some characters (the first occurrence).
deleteElementByData(tempList, 'e');
deleteElementByData(tempList, 'a');
deleteElementByData(tempList, 'o');
printList(tempList);
// Step 4. Insert to a given position.
insertElement(tempList, 'o', 1);
printList(tempList);
}
int main(){
insertDeleteTest();
system("pause");
}
注:这里的头节点是放了元素data的,所以按位置插入指的是按下标位置。
2.运行结果演示
二、钦版(int 类型)
1.结构体的创建
typedef struct _LinkNode
{
int data;
struct _LinkNode *next;//指向下一个结点的指针域
struct _LinkNode *prev;//指向上一个结点的指针域
}DbLinkNode,DbLinkList;
2.初始化
//初始化
bool DbInit_List(DbLinkList* &L)
{
L = new DbLinkNode ;//生成头结点,用头指针指向头结点
if(!L)
return false;
L->next = NULL;
L->prev = NULL;
return true;
}
3.头插法
//头插法
bool DbListInsertByHead(DbLinkList* &L,DbLinkNode* node)
{
if(!L||!node)
return false;
if(L->next)//若不为空
L->next->prev = node;//原来的第二个结点的prev 指向新的结点
node->next = L->next;//新节点的next指向原来的第二个结点
node->prev = L; //新节点的prev指向头结点
L->next = node; //头结点的next指向新节点,完成插入
return true;
}
4.尾插法
//尾插法
bool DbListInsertByTail(DbLinkList* &L,DbLinkNode* node)
{
DbLinkList *pMove = NULL;
if(!L || !node)
return false;
pMove = L;//指向表头
while(pMove->next)
{
pMove = pMove->next;
}
pMove->next = node;//原来的前一个结点指向新结点
node->next = NULL;//新结点成为尾结点,next指向空
node->prev = pMove; //新接点的指向
return true;
}
5.指定位置插入
//指定位置插入
bool LinkInsertByApoint(DbLinkList* &L,int i,int &e)//链表名,插入的位置,插入的数据
{
if(!L||!e||!L->next)
return false;
if(i<1)
return false;
int pos = 0;
DbLinkList *p,*s;
p=L;
while(p!=NULL && pos<i)//查找位置为i的结点 ,p指向该结点
{
p = p->next;
pos++;
}
if(!p|| pos != i)//p为空 or 为指向目标结点
{
cout<<"不存在结点:"<<i<<endl;
return false;
}
cout<<"即将插入的位置数据p:"<<p<<endl;
s = new DbLinkNode;
s->data = e;
s->next = p;//新结点的next指向目标位置结点
s->prev = p->prev;//新结点的prev指向原来目标位置结点的上一个结点
p->prev->next = s;//原来 指向目标位置结点的next 现在指向新节点
p->prev = s; //目标结点的prev指 向新节点
return true;
}
注:这里的位置是物理位置,不是下标位置。
6.统计元素个数
/*统计某个元素在链表中出现的次数*/
int Number(DbLinkList* &L,int data)
{
if(!L)
return 0;
int index = 0;
DbLinkNode *pMove = L;
while(pMove)
{
if(pMove->data == data)
index++;
pMove = pMove->next;
}
return index;
}
7.双向打印
//双向打印
bool PrintLink(DbLinkList* &L)
{
DbLinkNode *pMove = NULL;
if(!L)
{
cout<<"链表为空链表!!!";
return false;
}
pMove = L;//指向表头
cout<<"\n顺向打印:"<<endl;
while(pMove->next)
{
cout<<pMove->next->data<<" ";
pMove = pMove->next;
}
cout<<endl;
cout<<"\n逆向打印:"<<endl;
while(pMove&&pMove != L)//不打印表头
{
cout<<pMove->data<<" ";
pMove = pMove->prev;
}
cout<<endl;
return true;
}
既然是双链表,那么就要用好它双向的特性。
8.按位置删除
//按位置删除
bool LinkDeleteByPos(DbLinkList* &L,int i)
{
DbLinkList *p = L;//要删除的前面一个结点
int index = 0;
if(!L || !L->next)
{
return false;
}
if(i<1)
return false;// i < 1 时不合理
while(p && index<i) //p->next 指的是当前的结点
{
p = p->next;
index++;
}
if(!p)// 当 i>n 不合理
{
return false;
}
p->prev->next = p->next; //使目标结点的上一个结点的next指向目标结点的下一个
if(p->next)//为空就没必要再乱指向了
{
p->next->prev = p->prev; //使得被删除的结点的下一个结点的prev,指向被删除结点的上一个结点
}
delete p; //把被删除的结点的内存释放掉
return true;
}
9.按值删除
//按值删除
bool LinkDeleteByData(DbLinkList* &L,int e)
{
DbLinkList* p = L;
int num = Number(L,e);
for(int i = 0;i<num;i++)
{
DbLinkList* p = L;
while(p && p->data!=e)
{
p = p->next;
}//循环结束后p就是目标结点
if(!p)
return false;
p->prev->next = p->next;
if(p->next)
{
p->next->prev = p->prev;
}
delete p;
}
return true;
}
10.按值获取元素位置
//按值获取元素位置
bool LinkGetsDataByData(DbLinkNode* &L,int e,int &index)
{
DbLinkList *p = L->next; //p 当前结点
index = 1;
if(!L || !L->next)
{
return false;
}
while(p && p->data != e)
{
index++;
p=p->next;//p->next 是下一个结点
}
if(!p)
{
return false;
}
return true;
}
11.按位置获取元素值
//按位置获取数据
bool LinkGetsDataByPos(DbLinkNode* &L,int i,int &e)
{
int index = 1;
DbLinkList *p = L->next;
if(!L || !L->next)
{
cout<<"空玩意儿!";
return false;
}
while(p && index <i)//顺着链表向下扫描,不扫描链表头结点
{
p=p->next;
index++;//计数器
}
if(!p || index>i)
{
return false;//i值 不合法 :i>n 或 i<=0
}
e = p->data;
return true;
}
12.摧毁链表
//销毁链表
bool LinkDestroy(DbLinkList* &L)
{
DbLinkList *p = L;
while(p)
{
L= L->next;//指向下一个结点
delete p; //释放当前结点
p = L; //赋给p,让p去循环执行释放的工作
}
cout<<"\n销毁链表!!!" <<endl<<endl;
}
13.完整代码
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
typedef struct _LinkNode
{
int data;
struct _LinkNode *next;//下一个结点的指针域
struct _LinkNode *prev;//上一个结点的指针域
}DbLinkNode,DbLinkList;
//初始化
bool DbInit_List(DbLinkList* &L)
{
L = new DbLinkNode ;//生成头结点,用头指针指向头结点
if(!L)
return false;
L->next = NULL;
L->prev = NULL;
return true;
}
/*统计某个元素在链表中出现的次数*/
int Number(DbLinkList* &L,int data)
{
if(!L)
return 0;
int index = 0;
DbLinkNode *pMove = L;
while(pMove)
{
if(pMove->data == data)
index++;
pMove = pMove->next;
}
return index;
}
//头插法
bool DbListInsertByHead(DbLinkList* &L,DbLinkNode* node)
{
if(!L||!node)
return false;
// if(L->next == NULL)//只有表头
// {
// node->next = NULL;//新节点的next指向空
// node->prev = L;//新节点的prev指向头结点
// L->next = node;//头节点的next指向新节点
// }else{
// L->next->prev = node;//第二个结点的prev指向新结点
//
// node->next = L->next;//新结点的next指向第二个结点
// node->prev = L; //新节点的prev指向头结点
// L->next = node; //头结点的next 指向新结点
//
// }
if(L->next)//若不为空
L->next->prev = node;//第二个结点的prev 指向新的结点
node->next = L->next;//新节点的next指向第二个结点
node->prev = L; //新节点的prev指向头结点
L->next = node; //头结点的next指向新节点,完成插入
return true;
}
//尾插法
bool DbListInsertByTail(DbLinkList* &L,DbLinkNode* node)
{
DbLinkList *pMove = NULL;
if(!L || !node)
return false;
pMove = L;//指向表头
while(pMove->next)
{
pMove = pMove->next;
}
pMove->next = node;
node->next = NULL;
node->prev = pMove;
return true;
}
//指定位置插入
bool LinkInsertByApoint(DbLinkList* &L,int i,int &e)//链表名,插入的位置,插入的数据
{
if(!L||!e||!L->next)
return false;
if(i<1)
return false;
int pos = 0;
DbLinkList *p,*s;
p=L;
while(p!=NULL && pos<i)//查找位置为i的结点 ,p指向该结点
{
p = p->next;
pos++;
}
if(!p|| pos != i)//p为空 or 为指向目标结点
{
cout<<"不存在结点:"<<i<<endl;
return false;
}
cout<<"即将插入的位置数据p:"<<p<<endl;
s = new DbLinkNode;
s->data = e;
s->next = p;//新结点的next指向目标结点
s->prev = p->prev;//新结点的prev指向原来目标结点的上一个结点
p->prev->next = s;//原来 指向目标结点的next 指向新节点
p->prev = s; //原来目标结点的prev指 向新节点
return true;
}
//按位置获取数据
bool LinkGetsDataByPos(DbLinkNode* &L,int i,int &e)
{
int index = 1;
DbLinkList *p = L->next;
if(!L || !L->next)
{
cout<<"空玩意儿!";
return false;
}
while(p && index <i)//顺着链表向下扫描,不扫描链表头结点
{
p=p->next;
index++;//计数器
}
if(!p || index>i)
{
return false;//i值 不合法 :i>n 或 i<=0
}
e = p->data;
return true;
}
//按值获取元素位置
bool LinkGetsDataByData(DbLinkNode* &L,int e,int &index)
{
DbLinkList *p = L->next; //p 当前结点
index = 1;
if(!L || !L->next)
{
return false;
}
while(p && p->data != e)
{
index++;
p=p->next;//p->next 是下一个结点
}
if(!p)
{
return false;
}
return true;
}
//按位置删除
bool LinkDeleteByPos(DbLinkList* &L,int i)
{
DbLinkList *p = L;//要删除的前面一个结点
int index = 0;
if(!L || !L->next)
{
return false;
}
if(i<1)
return false;// i < 1 时不合理
while(p && index<i) //p->next 指的是当前的结点
{
p = p->next;
index++;
}
if(!p)// 当 i>n 不合理
{
return false;
}
p->prev->next = p->next; //使目标结点的上一个结点的next指向目标结点的下一个
if(p->next)//如果被删除结点的下一个结点不为空才操作。如果为空就没必要拿NULL去再乱指向了
{
p->next->prev = p->prev; //使得被删除的结点的下一个结点的prev,指向被删除结点的上一个结点
}
delete p; //把被删除的结点的内存释放掉
return true;
}
//按值删除
bool LinkDeleteByData(DbLinkList* &L,int e)
{
DbLinkList* p = L;
int num = Number(L,e);
for(int i = 0;i<num;i++)
{
DbLinkList* p = L;
while(p && p->data!=e)
{
p = p->next;
}//p就是目标结点
if(!p)
return false;
p->prev->next = p->next;
if(p->next)
{
p->next->prev = p->prev;
}
delete p;
}
return true;
}
//双向打印
bool PrintLink(DbLinkList* &L)
{
DbLinkNode *pMove = NULL;
if(!L)
{
cout<<"链表为空链表!!!";
return false;
}
pMove = L;//指向表头
cout<<"\n顺向打印:"<<endl;
while(pMove->next)
{
cout<<pMove->next->data<<" ";
pMove = pMove->next;
}
cout<<endl;
cout<<"\n逆向打印:"<<endl;
while(pMove&&pMove != L)//不打印表头
{
cout<<pMove->data<<" ";
pMove = pMove->prev;
}
cout<<endl;
return true;
}
//销毁链表
bool LinkDestroy(DbLinkList* &L)
{
DbLinkList *p = L;
while(p)
{
L= L->next;//指向下一个结点
delete p; //释放当前结点
p = L; //赋给p,让p去循环执行释放的工作
}
cout<<"\n销毁链表!!!" <<endl<<endl;
return true;
}
void Test()
{
DbLinkList *L = NULL;
DbLinkList *s = NULL;
//1.初始化双向链表
DbInit_List(L);
//2.头插法
int n;//一个变量多次使用输入,每次输入含义不同可能会出现,上一次输入过多,下一次想要不同含义的n,却使用了上一次过剩的n值了
cout<<"头插法创建单链表"<<endl;
cout<<"请输入插入元素个数:";
cin>>n;
printf("请依次输入%d个数据:\n",n);
while(n>0)
{
s = new DbLinkNode;
cin>>s->data;
DbListInsertByHead(L,s);
n--;
}
cout<<"\n\n头插法打印"<<endl;
PrintLink(L);
//3.尾插法
cout<<"\n\n尾插法创建单链表"<<endl;
cout<<"请输入插入元素个数:";
cin>>n;
printf("请依次输入%d个数据:",n);
while(n>0)
{
s = new DbLinkNode;
cin>>s->data;
DbListInsertByTail(L,s);
n--;
}
cout<<"\n\n尾插法打印"<<endl;
PrintLink(L);
//4.指定位置插入
int pos;
int e;
cout<<"\n请输入要插入的次数:";
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"\n请输入要插入的位置和数据:";
cin>>pos;
cin>>e;
if(LinkInsertByApoint(L,pos,e) == false)
{
cout<<"\n插入失败!!!\n";
PrintLink(L);
}else
{
cout<<"\n插入成功!\n";
PrintLink(L);
}
}
cout<<"\n\n任意位置插入打印"<<endl;
PrintLink(L);
//6.按位置获取元素
int element = 0;
cout<<"\n请输入要按位置获取的次数:";
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入要获取的元素的位置:"<<endl;
cin>>pos;
if(LinkGetsDataByPos(L,pos,element))
{
printf("\n获取第%d个元素成功!!!\n",pos);
cout<<"\n获取的数据为:"<<element<<endl<<endl;
}else
{
cout<<endl<<"获取失败!"<<endl;
}
}
//7.按值获取元素
int post = 0;
cout<<"\n请输入要按值获取的次数:";
cin>>n;
for(int i=0;i<n;i++)
{
cout<<"请输入要获取的元素的值:"<<endl;
cin>>element;
if(LinkGetsDataByPos(L,element,post))
{
printf("\n获取元素成功!!!");
cout<<"\n获取元素的链表位置为:"<<post<<endl;
}else
{
cout<<endl<<"获取失败!"<<endl;
}
}
//8.按位置删除
cout<<"\n请输入要按位置删除的次数:";
cin>>n;
bool flag;
for(int i=0;i<n;i++)
{
cout<<"\n请输入要删除的元素位置:";
cin>>post;
flag=LinkDeleteByPos(L,post);
if(flag)
{
printf("\n删除成功!\n");
PrintLink(L);
}else
{
cout<<"删除失败"<<endl;
}
}
int data,num=0;
cout<<endl<<"请输入要查询个数的元素值:";
cin>>data;
num=Number(L,data);
cout<<data<<"的个数为:"<<num;
int da;
cout<<endl<<"请输入要删除的值:";
cin>>da;
LinkDeleteByData(L,da);
PrintLink(L);
//9.销毁双链表
LinkDestroy(L);
}
int main()
{
Test();
system("pause");
return 0;
}
三、总结
1.双向链表具有双指针,可以利用此特性进行双向打印
2.由于每个结点有双指针域,那么在操作某一个结点时,循环指针可以直接循环指向该结点,通过结点本身的双指针特性进行更改指针域的操作。当然也可以循环到目标结点的上一个结点,再进行操作,只不过这时可能要定义多个指针来辅助操作,区别不大,看个人理解问题。
3.认识理解p->next的双重含义:
(1)p结点的下一个结点的地址(名词含义)
(2)p结点的next指针的指向(动词含义)