您的位置:首页 > 理论基础 > 数据结构算法

数据结构也不是那么没意思之双向循环链表

2016-06-30 14:06 375 查看
写数据结构的时候一定要注意 时刻在头脑中保存多个自己要写的数据结构的模型



1、头插入模型

2、尾插入模型

3、中间插入模型

4、对应的三个删除模型

双向循环链表的基本思路就是搞清楚

a、xx节点的后继的前驱 

b、xx节点的后继

c、xx节点的前驱的后继

d、xx节点的前驱

弄明白了这几个知识点问题迎刃而解(ab和cd可以选取这俩模式其中的一种来写双链表即可)

例如:

p->front->next = pnew; //这种模式插入后p向右偏移1
p->front = pnew;

//p->next->front = pnew;  这种模式插入后p向左偏移1
//p->next = pnew;

下面是全部的代码

Linux 3.10.0-229.el7.x86_64下可通过G++

/*************************************************************************
> File Name: a.cpp
> Author: ma6174
> Mail: ma6174@163.com
> Created Time: Wed 29 June 2016 11:54:56 AM CST
************************************************************************/

#include<iostream>
#include <stdio.h>
#include <stdlib.h>

class List
{
private:
class Node
{
public:
Node();
int number;
Node *front;
Node *next;
};
public:
List();

Node* malloc_node();  //分配内存空间
int free_node(Node *);      //释放内存空间
bool yes_or_no(Node *);  //检测内存空间是否分配成功
bool empty();  //判断链表是否为空 真代表空 假代表非空
int length(); //判断链表长度
int head_insert();  //头插入
int tail_insert();  //尾插入
int set_insert();  //任意位置插入
int head_delete(); //头删除
int tail_delete(); //尾删除
int set_delete();  //任意位置删除
int clear_List();  //清空链表内所有节点(非销毁)
int destory_list();  //销毁链表
void print_list();  //打印链表
private:
Node *head;
Node *tail;
Node *pnew;
};

int main()
{
List a;

a.head_insert();
a.print_list();

a.tail_insert();
a.print_list();

a.set_insert();
a.print_list();

a.head_delete();
a.print_list();

a.tail_delete();
a.print_list();

a.set_delete();
a.print_list();

return 0;
}

List::Node::Node() : number(-9999), front(NULL), next(NULL)
{
;
}

List::List()
{
int count = 0;

printf("请输入要初始化的节点个数:\n");
scanf("%d", &count);

if (count <= 0)
{
printf("error1\n");
exit(0);
}

if (yes_or_no(head = malloc_node()))
{
//循环双向链表为空的时候头节点应该自己指向自己
head->number = 0;    //在头节点中记录链表总节点个数
head->front = head;
head->next = head;
tail = head;

for (int i = 0; i < count; ++i)
{
if (yes_or_no(pnew = malloc_node()))
{
//插入思路就是《大话数据结构》83页的图1  还是那句话写数据结构之前脑子里要有对应的小模型
//这里选用的是尾插入模式概念
pnew->front = tail;  //新节点的前驱指向尾节点
pnew->next = tail->next;  //新节点的后继指向尾节点的下一个节点

tail->next->front = pnew; //尾节点的下一个节点的前驱指向新节点
tail->next = pnew;  //原本尾节点的后继指向新节点
tail = pnew;  //修正尾节点的位置
}
else
{
exit(1);
}
}

head->number = count;  //更新总节点数
}
else
{
exit(1);
}
}

List::Node* List::malloc_node()
{
return new Node;
}

int List::free_node(Node *node)
{
if (node != NULL)
{
delete node;
node = NULL;
--head->number;

return 0;
}

printf("error2\n");

return 1;
}

bool List::yes_or_no(Node *node)
{
return node != NULL ? true : false;
}

bool List::empty()
{
return head != tail ? true : false;
}

int List::length()
{
return head->number;
}

int List::head_insert()  //需要改变:pnew节点的前驱和后继   头节点的后继的的前驱  头节点的后继 (后两者顺序不可改变)
{
printf("头插入模式-》请输入要插入的数据:\n");

if (yes_or_no(pnew = malloc_node()))
{
scanf("%d",&pnew->number);

pnew->front = head;  //因为是头插入模式所以pnew的前驱指向头节点
pnew->next = head->next; //pnew的后继指向原链表中第一个节点(非头节点)

head->next->front = pnew;  //原本第一个节点的前驱指向pnew
head->next = pnew;  //头节点的后继指向pnew

++head->number;

return 0;
}

printf("error3\n");

return 1;
}

int List::tail_insert() //需要改变:pnew节点的前驱和后继   尾节点的后继节点的前驱  尾节点的后继 (后两者顺序不可改变)
{
printf("尾插入模式-》请输入要插入的数据:\n");

if (yes_or_no(pnew = malloc_node()))
{
scanf("%d", &pnew->number);

pnew->front = tail;  //pnew的前驱指向尾节点
pnew->next = tail->next; //pnew的后继指向尾节点的下一个节点也就是头节点

tail->next->front = pnew;  //尾节点的下一个节点(也就是头节点)的前驱指向pnew
tail->next = pnew;  //原本尾节点的后继指向pnew
tail = pnew;  //更新尾节点的指向

++head->number;

return 0;
}

printf("error4\n");

return 1;
}

int List::set_insert()
{
int location = 0;

printf("请输入要插入的位置:\n");
scanf("%d", &location);

if (location <= 0)
{
printf("error5\n");
return 1;
}

if (location == 1) //表明要插入在表头
{
head_insert();
}
else if (location >= head->number)  //表明要插入在表尾
{
tail_insert();
}
else   //表明要插入在location-1处的节点 和 location处的节点的中间
//需要改变:pnew节点的前驱和后继   location处节点前驱的后继  location处节点的前驱 (后两者顺序不可改变)
{
printf("请输入要插入的数据:\n");

if (yes_or_no(pnew = malloc_node()))
{
scanf("%d", &pnew->number);

Node *p = head->next;  //临时变量指向第一个节点

for (int i = 1; i < location && p != head; ++i, p = p->next)  //找到要插入的位置 i为1而不是0是因为元素个数开始是1而不是0
{
;//p表示要插入的位置
}

pnew->front = p->front;
pnew->next = p;

p->front->next = pnew; //这种模式插入后p向右偏移1
p->front = pnew;

//p->next->front = pnew;  这种模式插入后p向左偏移1
//p->next = pnew;

++head->number;

return 0;
}
}

printf("error6\n");

return 1;
}

int List::head_delete()
{
Node *p = head->next;

printf("头删除模式\n");

p->next->front = head; //第一个节点的下一个节点的前驱指向头节点
head->next = p->next;  //头节点的后继指向第一个节点的下一个节点

free_node(p);

return 0;
}

int List::tail_delete()
{
Node *p = tail;

printf("尾删除模式\n");

tail->front->next = head;  //最后一个节点的上一个节点的后继指向头节点
head->front = tail->front;  //头节点的前驱指向最后一个节点的上一个节点

tail = head->front;  //修正尾节点的位置  这就是循环双链表的好处

free_node(p);

return 0;
}

int List::set_delete()
{
Node *p = head->next;
int number = -9999;

printf("请输入要删除的元素:\n");
scanf("%d", &number);

if (head->next->number == number) //表明要删除的元素在链表头
{
head_delete();
}
else if (head->front->number == number) //表明要删除的元素在链表尾
{
tail_delete();
}
else //表明要删除的元素在链表中间
{
while (p->next != head && p->number != number)
{
p = p->next;  //p表示要删除的节点位置
}

p->front->next = p->next;  //要删除的节点的前一个节点的后继指向了要删除的节点的下一个节点 例如1 2 3 要删除2 那么1的后继要指向3
p->next->front = p->front; //要删除的节点的下一个节点的前驱指向了要删除的节点的前一个节点 例如1 2 3 要删除2 那么3的前驱要指向1

free_node(p);
}

return 0;
}

int List::clear_List()
{
for (Node *p = head->next; p != head; p=p->next)
{
p->number = -9999;
}

return 0;
}

int List::destory_list()
{
for (Node *p = head->next, *q = p; q != head; p = q)
{
q = p->next;
free_node(p);
}

return 0;
}

void List::print_list()
{
printf("链表中总节点数为: %d\n", head->number);

for (Node *p = head->next; p != head; p = p->next)
{
printf("双向循环链表中的元素为: %d\n", p->number);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  数据结构 链表