【数据结构与算法——C语言】“链表操作与算法”之“负值结点前移”

本文介绍了如何使用Dev-C++设计并实现一个算法,将给定单链表中所有小于零的节点移动到所有大于等于零的节点前面,通过伪代码、流程图和代码段详细展示了设计过程和调试测试案例。

1. 实验内容及上机实验所用平台

1.1 实验内容

某非空单链表 L 中所有元素为整数,设计一个算法将所有小于零的节点移到所有大于等于零的节点的前面。注意测试负值结点是表头、表尾元素结点的不同情况

1.2 设计思路

由例子来设计算法。指针 last 永远指向开头的最后一个负值,通过指针 p、q、last 来移动结点,将负值从链表中提取出来,再将负值插入到正值前面。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3 实验平台软件

Dev-C++.

2. 数据结构

非空单链表,有指向后继结点的 next 指针,尾结点的 next 域置为 NULL

3. 设计描述与分析

3.1 伪代码

输入:字符串 str
输出:移动后新的单链表。

// 将字符串str转化为数组num 
// 利用数组num创建循环单链表 
// 生成结点*last ← head, *p ← head→next, *q ← (p → next)
// 跳过以负整数开头的结点
while p!= NULL
    if p指向的值 ≤ 0
        // 将此结点移动到正值结点前面
        // 指针进行适当移动(见设计思路步骤)
     end if
    else
        q ← (q → next)
       	p ← (q → next)
     end else
 end while

3.2 流程图

在这里插入图片描述

3.3 主要代码段

void Move() {
	LinkNode *last = head;
	LinkNode *p = head -> next;
	while (p != NULL && p -> val < 0) { // 跳过以负整数开头的结点
		last = last -> next;
		p = p -> next;
	}
	LinkNode *q = last;
	while (p != NULL) {
		if (p -> val < 0) { // 找到负整数结点 p 
			q -> next = p -> next; // 删除 p 结点 
			p -> next = last -> next; // 将结点 p 插入到 last 结点后 
			last -> next = p; // 连接单链表 
			last = p; // 结点 last 后移 1 个结点 
			p = q -> next; // 结点 p 指向 last 结点的后继结点 
		}
		else { // 结点 p 不是 负整数结点,所以结点 last、p 后移一个结点 
			q = q -> next;
			p = q -> next;
		}
	}
}

3.4 源代码

需要先在源代码目录下新建 in.txt 文件,在此文件下输入要测试的数据。

#include <iostream>
#include <string> 
using namespace std;

struct LinkNode {	// 不带头结点的单链表结点类型
	int val;
	LinkNode *next;
	LinkNode():next(NULL) {}	// 构造函数 
	LinkNode(int x): val(x), next(NULL) {}	// 重载构造函数 
};

class LinkList {
public:
	LinkNode *head;	// 	单链表的头结点 
	LinkList()	// 构造函数,创建一个空单链表
	{
		head = new LinkNode();
	}
	void CreateList(int a[], int n) {	// 建立循环单链表
		LinkNode *s, *r = head;	// r为尾结点指针,开始时指向头结点
		for (int i = 0; i < n; i++) {
			s = new LinkNode(a[i]);	// 创建数据结点s
			r -> next = s;	// 将 s 结点链接到末尾 
			r = s;
		}
		r -> next = NULL;	// 将尾结点的 next 域置为 NULL 
	}
	
	void Move() {
		LinkNode *last = head;
		LinkNode *p = head -> next;
		while (p != NULL && p -> val < 0) { // 跳过以负整数开头的结点
			last = last -> next;
			p = p -> next;
		}
		LinkNode *q = last;
		while (p != NULL) {
			if (p -> val < 0) { // 找到负整数结点 p 
				q -> next = p -> next; // 删除 p 结点 
				p -> next = last -> next; // 将结点 p 插入到 last 结点后 
				last -> next = p; // 连接单链表 
				last = p; // 结点 last 后移 1 个结点 
				p = q -> next; // 结点 p 指向 last 结点的后继结点 
			}
			else { // 结点 p 不是 负整数结点,所以结点 last、p 后移一个结点 
				q = q -> next;
				p = q -> next;
			}
		}
	}
	
	void DispList()	// 输出单链表所有结点值
	{
		LinkNode *p;
		p = head -> next;	// p指向开始结点
		while (p != NULL)	// p不为 NULL,输出 p 结点的 val
		{
			cout << p -> val << " ";
			p = p -> next;	// p移向下一个结点
		}
		cout << endl;
	}
};

int main() {
	cout << "\t\t第4题 - 负值节点前移\n\n";
	cout << "---------------------------------------------------\n\n";
	freopen("in.txt", "r", stdin);
	string str;
	while (cin >> str);	// 读取文件数据(当字符串读取) 
	cout << "样例输入:" << str << endl;
	
	string str_temp = ""; 
    int num_temp[1001] = {0}, num[1001] = {0}, cnt_temp = 0, cnt = 0, x = 0;
    bool flag = false;	// 正数
    
    for(int i = 0; i < str.length(); i++) num_temp[cnt_temp++] = str[i] - '0';	// 将字符全部转为数字 
    // 提取字符串中的数字 
    for (int i = 0; i < cnt_temp; i++) {
    	if (num_temp[i] >= 0 && num_temp[i] <= 9) x = x * 10 + num_temp[i];
		else if (num_temp[i] == -3) flag = true;
		else {
			if (flag) {
				x = -x;
				flag = false;
			}
			num[cnt++] = x;
			x = 0;
			i++;
		}
		if (i == cnt_temp - 1) {
			if (flag) {
				x = -x;
				flag = false;
			}
			num[cnt++] = x;
		}
	}
	
	LinkList L;
	L.CreateList(num, cnt);	// 创建循环单链表 
	cout << "\n\n样例输出:";
	L.Move();
	L.DispList();
	
	cout << "\n\n"; 
	freopen("CON", "r", stdin);	// 为了可直接查看exe可执行文件,需要将权限返回键盘 
	system("pause"); 
	return 0;
}

4. 调试过程

  1. 测试1
    a) 测试数据用例:9,8,-6,7,4,-3,-6
    b) 测试数据用例1的特点:该数据用例中负值结点出现在表中和表尾。
    c) 测试结果:
    在这里插入图片描述

  2. 测试2
    a) 测试数据用例:-3,9,8,-5,7,4
    b) 测试数据用例2的特点:该数据用例中负值结点出现在表首和表中。
    c) 测试结果:
    在这里插入图片描述

5. 实验总结

在本次实验中,最重要的是如何通过 3 个指针的移动来实现题目的要求。而最好的方法就是拿一个比较全面的例子来模拟它的移动情况,通过模拟的情况来判定指针要如何移动。同样本题还是输入的一个字符串,至于如何能够有效的分割出来我们要的数字,还需要之后继续研究。

评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值