【数据结构与算法】C++用IDE刷题的正确姿势

本文介绍了一种组织链表算法题解的有效方法,包括代码结构的设计思路,如何创建链表节点,实现特定链表操作(如节点翻转),以及如何构建测试用例。

有时候为了方便自测,自用,想要把接口做一些小改变(涉及输入输出),亦或是想要方便归档整理,我们会自己使用IDE编写代码来刷题。

一般一种类型的题目,比如链表,我们可以放在一个工程下面。但是刷的题多了,很容易就会混乱,这时候需要良好的代码结构来进行梳理。

代码结构

以链表为例,一个链表类型题的工程应包含:构建链表的头文件:listNode.h,主函数源文件:main.cpp,各个题目对应一组的头文件和源文件
在这里插入图片描述

构建链表的头文件:listNode.h

这个文件主要包含一个结构体 ListNode 用于构建链表的一个节点,以及一个函数 createListNode 用于通过 vector 容器构建一个链表,代码如下:

#ifndef LISTNODE_LISTNODE
#define LISTNODE_LISTNODE
#include<iostream>
#include<vector>

using namespace std;

struct ListNode 
{
	int val;
	ListNode* next;
	ListNode() : val(0), next(NULL) {}
	ListNode(int x) : val(x), next(NULL) {}
	ListNode(int x, ListNode* next) : val(x), next(next) {}
	void printListNode()
	{
		cout << val << " ";
		ListNode* head = next;
		while (head)
		{
			cout << head->val << " ";
			head = head->next;
		}
		cout << endl;
	}
};

template<class T>
ListNode* createListNode(vector<T> data)
{
	ListNode* head;
	head = new ListNode(data[0]);

	ListNode* tempPtr;
	tempPtr = head;

	for (unsigned int i = 1; i < data.size(); ++i)
	{
		ListNode* tempNode;
		tempNode = new ListNode(data[i]);
		tempPtr->next = tempNode;
		tempPtr = tempPtr->next;
	}
	return head;
}

#endif

为了方便输出整个链表,我在 ListNode 增加了一个函数作为结构体成员,用于输出整个链表的值
目前这个链表只支持 int 型,后面有需要再套个模板类以适应不同的类型

各个题目对应一组的头文件和源文件

这里以链表中的节点每k个一组翻转为例
头文件为 ReverseKGroup.h, 对应的源文件为 ReverseKGroup.cpp,源码如下:

#ifndef LISTNODE_REVERSEKGROUP
#define LISTNODE_REVERSEKGROUP
#include"listNode.h"

using namespace std;

/*
将给出的链表中的节点每 k 个一组翻转,返回翻转后的链表
如果链表中的节点数不是 k 的倍数,将最后剩下的节点保持原样
你不能更改节点中的值,只能更改节点本身。

数据范围: 2000 0≤n≤2000 , 1≤k≤2000 ,链表中每个元素都满足 0≤val≤1000
要求空间复杂度 O(1)O(1),时间复杂度 O(n)O(n)
例如:
给定的链表是 1→2→3→4→5
对于 k = 2k=2 , 你应该返回 2→1→4→3→5
对于 k = 3k=3 , 你应该返回 3→2→1→4→5
*/

class ReverseKGroup
{
public:
	bool integralLN = true;
	ListNode* endNode = nullptr;
	ListNode* tempHead = nullptr;
	ListNode* reverseKGroup(ListNode* head, int k);
	ListNode* reverse(ListNode* head, int n);
};

#endif

#include "ReverseKGroup.h"

ListNode* ReverseKGroup::reverseKGroup(ListNode* head, int k) 
{
	if (head == nullptr || k == 1) return head;
	head = reverse(head, k);
	while (integralLN) 
	{
		ListNode* concatHead = tempHead;
		ListNode* tempPointer = endNode;					// 下一段反转的起点
		tempPointer = reverse(tempPointer, k);
		if (integralLN) concatHead->next = tempPointer;		// 上一段和这一段拼接
	}
	return head;
}

ListNode* ReverseKGroup::reverse(ListNode* head, int n)
{
	if (head == nullptr) 
	{
		integralLN = false;
		return head;
	}
	if (n == 1) 
	{
		endNode = head->next;
		return head;
	}
	ListNode* node = reverse(head->next, n - 1);
	if (integralLN)
	{
		head->next->next = head;
		head->next = endNode;
		tempHead = head;									// 用于下一段拼接的头
		return node;
	}
	else 
	{
		return head;
	}
}

头文件用于inlcude需要的头文件,描述题目内容,方便复习,声明用来解题的类
源文件就拿来写解题用的代码,这部分跟浏览器里用 leetcode 一类的题库提供的集成环境差不多,但debug起来可方便太多了

主函数源文件:main.cpp

主函数源文件用于创建测试用例,并执行测试程序,代码如下:

// listNode.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
//

#include <iostream>
#include <vector>
#include "listNode.h"
#include "ReverseListRecursion.h"
#include "ReverseKGroup.h"
#include "ReverseBetween.h"
#include "merge.h"

using namespace std;

int main()
{
	vector<int> inputData = { 1, 2, 3, 4, 5 ,6, 7 };
	vector<int> inputData_2 = { 3, 4, 5 ,6, 7, 8, 9 };
	ListNode* head = createListNode<int>(inputData);
	ListNode* head_2 = createListNode<int>(inputData_2);
	ListNode* printHead = head;
	ListNode* printHead_2 = head_2;
	head->printListNode();
	head_2->printListNode();

	//ReverseListRecursion reverse;
	//head = reverse.tureReturn(head);
	//head->printListNode();

	//ReverseBetween reverse;
	//head = reverse.reverseBetween(head, 2, 4);
	//head->printListNode();

	ReverseKGroup reverse;
	head = reverse.reverseKGroup(head, 3);
	head->printListNode();

	//MergeListNode merge;
	//ListNode* newHead = merge.merge(head, head_2);
	//newHead->printListNode();

	return 0;
}

这样的代码结构比较简洁,逻辑清晰,各个题目互不干扰,并且可以很方便地进行归档整理,一道题就对应一组头文件和源文件,复习起来也很方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值