DS之单链表

来源:互联网 发布:电脑连不上网络 编辑:程序博客网 时间:2024/05/18 12:04

问题 A: DS单链表--类实现

时间限制: 1 Sec  内存限制: 128 MB
提交: 573  解决: 201
[提交][状态][讨论版]

题目描述

用C++语言和类实现单链表,含头结点

属性包括:data数据域、next指针域

操作包括:插入、删除、查找

注意:单链表不是数组,所以位置从1开始对应首结点,头结点不放数据

类定义参考

输入

n

第1行先输入n表示有n个数据,接着输入n个数据
第2行输入要插入的位置和新数据
第3行输入要插入的位置和新数据
第4行输入要删除的位置
第5行输入要删除的位置
第6行输入要查找的位置
第7行输入要查找的位置

输出

n

数据之间用空格隔开,

第1行输出创建后的单链表的数据

每成功执行一次操作(插入或删除),输出执行后的单链表数据

每成功执行一次查找,输出查找到的数据

如果执行操作失败(包括插入、删除、查找等失败),输出字符串error,不必输出单链表

样例输入

6 11 22 33 44 55 66
3 777
1 888
1
11
0
5

样例输出

11 22 33 44 55 66 
11 22 777 33 44 55 66 
888 11 22 777 33 44 55 66 
11 22 777 33 44 55 66 
error
error
44

/** *单链表的类实现我一共用了两种方法来写,区别在于插入和删除这两个函数。这个版本是方法1 *同时,在单链表的初始化,我也用了两种方法,一种是调用插入函数,一种是利用尾插法,在构造函数里面循环,从而完成单链表的初始化。原理大同小异。 * *插入函数和删除函数都是要检查第i - 1个结点,也就是直接前驱结点的指针。同时,插入函数要确保前一个结点是存在的,才能使前面的结点指向我们要插入的结点。 *而删除函数要确保前一个结点的后一个结点是存在的,也就是我们要删除的结点是存在的,这就是在两个函数里面while循环中,插入函数是检查p非空和删除函数中检查p->next非空的原因。(p是直接前驱结点) * *插入函数: *而我在方法二中是首先检查我要插入和删除的值是否超出范围,如果超出返回error。相比方法一更容易理解。 *但是要注意的是,插入函数是可以插入最后一个位置,也就是长度+1的位置,所以这个对于方法二的判断条件很重要。 *因为方法一是直接检查插入的前一个结点是否为空,不为空就可以插入,所以链表的最后一个结点是不为空的,就可以插入新的结点。 * *删除函数: *删除函数我犯了一个很大的错误,然后卡了很久,就是删错结点了。。在我第一次写的时候是这样的: *q = p->next->next;p->next = q;delete q; (p为要删除的结点的直接前驱结点) *这样导致删除的是要删除的结点的下一个节点了,虽然看上去好像是对的,输出也是正确的,但是提交之后会发现答案错误。这个问题我在第二天重新看一遍才发现的。。 * */#include <iostream>using namespace std;#define ok 0#define error -1// 链表结点类定义 class ListNode {public:int data;ListNode *next;ListNode() {next = NULL;} }; // 带头结点的单链表类定义class LinkList {public:ListNode *head;int len;LinkList() {head = new ListNode;len = 0;}LinkList(int n) {head = new ListNode;len = 0;/*for(int i = 1; i <= n; i++) {int item;cin >> item;this->LL_insert(i, item);}*/ListNode *tail = head;ListNode *p;for(int i = 0; i < n; i++) {int item;cin >> item;p = new ListNode;p->data = item;p->next = NULL;tail->next = p;tail = p;len++;}}~LinkList() {ListNode *p, *q;p = head;while(p != NULL) {q = p;p = p->next;delete q;}len = 0;head = NULL;}void LL_display() {ListNode *p;p = head->next;while(p) {cout << p->data << " ";p = p->next;}cout << endl; }ListNode *LL_index(int i) {ListNode *p = head;if(i > len || i < 1) {return NULL;}for(int j = 0; j < i; j++) {p = p->next;}return p;}int LL_get(int i) {return this->LL_index(i)->data;} int LL_insert(int i, int item) {ListNode *p = head;int j = 0;while(p && j < i - 1) {// 寻找i - 1个结点,并且插入的位置的前一个必须要有东西,这样才能使前一个节点的后一个等于我们要插入的节点 p = p->next;j++;}if(!p || j > i - 1) {// 如果要插入的前一个结点为空,或者一开始插入的地方就是负数或大于i-1 return error;}ListNode *s = new ListNode;s->data = item;s->next = p->next;p->next = s;len++;return ok;}int LL_del(int i) {ListNode *p = head;int j = 0;while(p->next && j < i - 1) {// 寻找i - 1个结点,并且删除前一个节点的后一个位置,所以要确保p->next有东西 p = p->next;j++;}if(!(p->next) || j > i - 1) {return error;}ListNode *q = p->next;p->next = q->next;delete q;len--;return ok;} }; int main() {int n;cin >> n;LinkList list(n);list.LL_display();for(int i = 0; i < 2; i++) {int j, item;cin >> j >> item;if(!list.LL_insert(j ,item))list.LL_display();elsecout << "error" << endl;}for(int i = 0; i < 2; i++) {int j;cin >> j;if(!list.LL_del(j))list.LL_display();elsecout << "error" << endl;}for(int i = 0; i < 2; i++) {int j;cin >> j;if(list.LL_index(j) != NULL)cout << list.LL_get(j) << endl;elsecout << "error" << endl;}

因为具体方法大同小异,所以我这里就只贴出插入和删除函数的不同

/** *要注意的点和总结都写到了方法一中啦,可以去看看嘻嘻 */
int LL_insert(int i, int item) {if(i < 1 || i > len + 1) {return error;}ListNode *p = head;for(int j = 0; j < i - 1; j++) {p = p->next;} ListNode *s = new ListNode;s->data = item;s->next = p->next;p->next = s;len++;return ok;} int LL_del(int i) {if(i < 1 || i > len) {return error;} ListNode *p = head;for(int j = 0; j < i - 1; j++) {p = p->next;}ListNode *q = p->next;p->next = q->next;delete q;len--;return ok;}

问题 B: DS单链表--结点交换

时间限制: 1 Sec  内存限制: 128 MB
提交: 275  解决: 174
[提交][状态][讨论版]

题目描述

用C++实现含头结点的单链表,然后实现单链表的两个结点交换位置。

注意不能简单交换两个结点包含数据,必须通过修改指针来实现两个结点的位置交换

交换函数定义可以参考:

swap(int  pa, int pb)  //pa和pb表示两个结点在单链表的位置序号

swap (ListNode * p, ListNode * q)  //p和q表示指向两个结点的指针

输入

第1行先输入n表示有n个数据,接着输入n个数据

第2行输入要交换的两个结点位置

第3行输入要交换的两个结点位置

输出

第一行输出单链表创建后的所有数据,数据之间用空格隔开

第二行输出执行第1次交换操作后的单链表数据,数据之间用空格隔开

第三行输出执行第2次交换操作后的单链表数据,数据之间用空格隔开

如果发现输入位置不合法,输出字符串error,不必输出单链表

样例输入

5 11 22 33 44 55
1 4
2 6

样例输出

11 22 33 44 55 
44 22 33 11 55 
error

提示

注意要用链表实现哦!


/** *问题B我是在方法1上的基础直接添加函数修改的。 *这次卡在的地方是,交换结点的逻辑没有搞清楚。。 *交换结点的要点是,使直接前驱结点指向要交换的结点,要交换的结点的next指针,要指向原来结点指向的下一个位置。 *一开始我用了两个结点来保存当前要交换的结点,但是在多次尝试后,我试出了死循环还有链表被缩短的情况。 *后来直接输出了几个结点的地址,发现第一次交换的时候,temp1或2的地址就会发生改变了,第一次完成交换后,此时如果直接使用temp1或2的值会发现已经被改变了,所以就出现了链表“被吃掉”的情况了。 *(不知道有没有描述清楚,总之我想表达的是那些值可能已经改变啦,如果实在不理解,可以把那些值单个输出试一试,发现我们想要就交换的东西的地址已经不是原来那个值啦) *然后我的解决方法是再引入一个temp3,这样使得其中一个要交换的结点的直接后继结点被保存了下来。有没有更简洁的办法目前没有想到emmmmmmm **/#include <iostream>using namespace std;#define ok 0#define error -1// 链表结点类定义 class ListNode {public:int data;ListNode *next;ListNode() {next = NULL;} }; // 带头结点的单链表类定义class LinkList {public:ListNode *head;int len;LinkList() {head = new ListNode;len = 0;}LinkList(int n) {head = new ListNode;len = 0;for(int i = 1; i <= n; i++) {int item;cin >> item;this->LL_insert(i, item);}/*ListNode *tail = head;ListNode *p;for(int i = 0; i < n; i++) {int item;cin >> item;p = new ListNode;p->data = item;p->next = NULL;tail->next = p;tail = p;len++;}*/}~LinkList() {ListNode *p, *q;p = head;while(p != NULL) {q = p;p = p->next;delete q;}len = 0;head = NULL;}void LL_display() {ListNode *p;p = head->next;while(p) {cout << p->data << " ";p = p->next;}cout << endl; }ListNode *LL_index(int i) {ListNode *p = head;if(i > len || i < 1) {return NULL;}for(int j = 0; j < i; j++) {p = p->next;}return p;}int LL_get(int i) {return this->LL_index(i)->data;} int LL_insert(int i, int item) {ListNode *p = head;int j = 0;while(p && j < i - 1) {// 寻找i - 1个结点,并且插入的位置的前一个必须要有东西,这样才能使前一个节点的后一个等于我们要插入的节点 p = p->next;j++;}if(!p || j > i - 1) {// 如果要插入的前一个结点为空,或者一开始插入的地方就是负数或大于i-1 return error;}ListNode *s = new ListNode;s->data = item;s->next = p->next;p->next = s;len++;return ok;}int LL_del(int i) {ListNode *p = head;int j = 0;while(p->next && j < i - 1) {// 寻找i - 1个结点,并且删除前一个节点的后一个位置,所以要确保p->next有东西 p = p->next;j++;}if(!(p->next) || j > i - 1) {return error;}ListNode *q = p->next;p->next = q->next;delete q;len--;return ok;} int Swap(int pa, int pb) {if((pa < 1 || pa > len) || (pb < 1 || pb > len))return error;ListNode *p = head;ListNode *q = head;for(int i = 0; i < len; i++) {if(i != pa - 1 && i < pa - 1) {p = p->next;} if(i != pb - 1 && i < pb - 1) {q = q->next;}}ListNode *temp1, *temp2, *temp3;temp1 = p->next;temp2 = q->next;temp3 = q->next->next;p->next = temp2;p->next->next = temp1->next;q->next = temp1;q->next->next = temp3;return ok;} }; int main() {int n;cin >> n;LinkList list(n);list.LL_display();for(int i = 0; i < 2; i++) {int pa, pb;cin >> pa >> pb;if(!list.Swap(pa,pb))list.LL_display();elsecout << "error" << endl; }return 0;}

为了方便理解我画了一个图。


【2017-9-18更新】
/** *问题B这次我直接用写好的index函数,但是报错了。后来找回到index函数发现只能从1开始查找,但是我如果在Swap函数中要用的index必须要使用他的前驱结点,故这里我把index稍作修改,就可以在Swap函数中使用了(因为头结点也算一个结点嘛)。 **/ListNode *LL_index(int i) {ListNode *p = head;if(i > len || i < 0) {// 在交换函数中用到index函数的话,这里要做一点修改,因为要交换第一个结点的话,头结点是有东西可以指向的,所以把这里i < 1改为i < 0,就可以在Swap函数中使用index函数了  return NULL;}for(int j = 0; j < i; j++) {p = p->next;}return p;}int Swap(int pa, int pb) {if((pa < 1 || pa > len) || (pb < 1 || pb > len))return error;/*ListNode *p = head;ListNode *q = head;for(int i = 0; i < len; i++) {if(i != pa - 1 && i < pa - 1) {p = p->next;} if(i != pb - 1 && i < pb - 1) {q = q->next;}}*/ListNode *p = this->LL_index(pa - 1);// 这个地方一开始报错,已修改index函数 ListNode *q = this->LL_index(pb - 1);ListNode *temp1, *temp2, *temp3;temp1 = p->next;temp2 = q->next;temp3 = q->next->next;p->next = temp2;p->next->next = temp1->next;q->next = temp1;q->next->next = temp3;return ok;}


问题 C: DS单链表--合并

时间限制: 1 Sec  内存限制: 128 MB
提交: 248  解决: 173
[提交][状态][讨论版]

题目描述

假定两个单链表是递增有序,定义并实现以下函数,完成两个单链表的合并,继续保持递增有序

int LL_merge(ListNode *La, ListNode *Lb)

输入

第1行先输入n表示有n个数据,接着输入n个数据
第2行先输入m表示有M个数据,接着输入m个数据

输出

输出合并后的单链表数据,数据之间用空格隔开

样例输入

3 11 33 55
4 22 44 66 88

样例输出

11 22 33 44 55 66 88


/** *在归并两个链表为一个链表的时候,不需要另建新表的空间,而只需讲原来两个链表中结点之间的关系解除,重新按照元素值非递增的关系将所有结点链接成一个链表即可。 */#include <iostream>using namespace std;#define ok 0#define error -1// 链表结点类定义 class ListNode {public:int data;ListNode *next;ListNode() {next = NULL;} }; // 带头结点的单链表类定义class LinkList {public:ListNode *head;int len;LinkList() {head = new ListNode;len = 0;}LinkList(int n) {head = new ListNode;len = 0;for(int i = 1; i <= n; i++) {int item;cin >> item;this->LL_insert(i, item);}/*ListNode *tail = head;ListNode *p;for(int i = 0; i < n; i++) {int item;cin >> item;p = new ListNode;p->data = item;p->next = NULL;tail->next = p;tail = p;len++;}*/}~LinkList() {ListNode *p, *q;p = head;while(p != NULL) {q = p;p = p->next;delete q;}len = 0;head = NULL;}void LL_display() {ListNode *p;p = head->next;while(p) {cout << p->data << " ";p = p->next;}cout << endl; }ListNode *LL_index(int i) {ListNode *p = head;if(i > len || i < 1) {return NULL;}for(int j = 0; j < i; j++) {p = p->next;}return p;}int LL_get(int i) {return this->LL_index(i)->data;} int LL_insert(int i, int item) {ListNode *p = head;int j = 0;while(p && j < i - 1) {// 寻找i - 1个结点,并且插入的位置的前一个必须要有东西,这样才能使前一个节点的后一个等于我们要插入的节点 p = p->next;j++;}if(!p || j > i - 1) {// 如果要插入的前一个结点为空,或者一开始插入的地方就是负数或大于i-1 return error;}ListNode *s = new ListNode;s->data = item;s->next = p->next;p->next = s;len++;return ok;}int LL_del(int i) {ListNode *p = head;int j = 0;while(p->next && j < i - 1) {// 寻找i - 1个结点,并且删除前一个节点的后一个位置,所以要确保p->next有东西 p = p->next;j++;}if(!(p->next) || j > i - 1) {return error;}ListNode *q = p->next;p->next = q->next;delete q;len--;return ok;} int Swap(int pa, int pb) {if((pa < 1 || pa > len) || (pb < 1 || pb > len))return error;ListNode *p = head;ListNode *q = head;for(int i = 0; i < len; i++) {if(i != pa - 1 && i < pa - 1) {p = p->next;} if(i != pb - 1 && i < pb - 1) {q = q->next;}}ListNode *temp1, *temp2, *temp3;temp1 = p->next;temp2 = q->next;temp3 = q->next->next;p->next = temp2;p->next->next = temp1->next;q->next = temp1;q->next->next = temp3;return ok;}int LL_merge(ListNode *La, ListNode *Lb) {ListNode *p = La->next;ListNode *q = Lb->next;ListNode *s = La;while(p && q) {if(p->data <= q->data) {s->next = p;s = p;p = p->next;} else {s->next = q;s = q;q = q->next;}}s->next = p ? p : q; // 条件运算符的优先级高于赋值运算符,先算p?p:q,如p不为0整个条件表达式的值为p,否则为q。再把条件表达式的值赋给s->next。p = s;}  }; int main() {int n;cin >> n;LinkList list_1(n);cin >> n;LinkList list_2(n);list_1.LL_merge(list_1.head, list_2.head);list_1.LL_display();return 0;}