问题 A: DS单链表--类实现
时间限制: 1 Sec 内存限制: 128 MB提交: 573 解决: 201
[ 提交][ 状态][ 讨论版]
题目描述
用C++语言和类实现单链表,含头结点
属性包括:data数据域、next指针域
操作包括:插入、删除、查找
注意:单链表不是数组,所以位置从1开始对应首结点,头结点不放数据
类定义参考
输入
第2行输入要插入的位置和新数据
第3行输入要插入的位置和新数据
第4行输入要删除的位置
第5行输入要删除的位置
第6行输入要查找的位置
第7行输入要查找的位置
输出
数据之间用空格隔开,
第1行输出创建后的单链表的数据
每成功执行一次操作(插入或删除),输出执行后的单链表数据
每成功执行一次查找,输出查找到的数据
如果执行操作失败(包括插入、删除、查找等失败),输出字符串error,不必输出单链表
样例输入
样例输出
/**
* 单链表的类实现我一共用了两种方法来写,区别在于插入和删除这两个函数。这个版本是方法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();
else
cout << "error" << endl;
}
for(int i = 0; i < 2; i++) {
int j;
cin >> j;
if(!list.LL_del(j))
list.LL_display();
else
cout << "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;
else
cout << "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,不必输出单链表
样例输入
样例输出
提示
注意要用链表实现哦!
/**
* 问题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();
else
cout << "error" << endl;
}
return 0;
}
为了方便理解我画了一个图。
/**
* 问题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)
输入
输出
输出合并后的单链表数据,数据之间用空格隔开
样例输入
样例输出
/**
* 在归并两个链表为一个链表的时候,不需要另建新表的空间,而只需讲原来两个链表中结点之间的关系解除,重新按照元素值非递增的关系将所有结点链接成一个链表即可。
*/
#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;
}