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

来源:互联网 发布:印刷厂淘宝简介 编辑:程序博客网 时间:2024/05/07 20:36

写数据结构的时候一定要注意 时刻在头脑中保存多个自己要写的数据结构的模型

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;  //原本第一个节点的前驱指向pnewhead->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;  //尾节点的下一个节点(也就是头节点)的前驱指向pnewtail->next = pnew;  //原本尾节点的后继指向pnewtail = 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向右偏移1p->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的后继要指向3p->next->front = p->front; //要删除的节点的下一个节点的前驱指向了要删除的节点的前一个节点 例如1 2 3 要删除2 那么3的前驱要指向1free_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);}}


0 0