1. 程式人生 > 實用技巧 >Huawei機試系列(2)

Huawei機試系列(2)

從單向連結串列中刪除指定值的節點

問題描述詳見 從單向連結串列中刪除指定值的節點

  1. 自己實現一個單鏈表,插入和刪除
  2. 注意頭節點,輸出格式
#include <iostream>
using namespace std;
struct Node {
	int elem;
	Node* next;
	Node(int e) :elem(e), next(nullptr) {}
};

class linkList {
private:
	Node* head;
public:
	linkList(int e) :head(new Node(e)) {}

	void insert(int val, int pos) {
		Node* it = head;
		while (it->elem != pos) {  //找到插入節點的前一個位置
			if (it->next)
				it = it->next;
			else
				return;
		}
		Node* node = new Node(val);
		node->next = it->next;
		it->next = node;
	}
	void remove(int pos) {
		Node* it, * temp;

		if (head->elem == pos)
		{
			temp = head;
			head = head->next;
			delete temp;
		}
		it = head;
		while (it->next && it->next->elem != pos)
			it = it->next;
		if (it->next)
		{
			temp = it->next;
			it->next = it->next->next;
			delete temp;
		}
	}
	void printList() {
		Node* it = head;
		while (it != nullptr)
		{
			cout << it->elem << ' ';
			it = it->next;
		}
		cout << endl;
	}
};

int main()
{
	int nodeNum, headNode, v, p, delNode;
	while (cin >> nodeNum >> headNode) {
		linkList* myList = new linkList(headNode);
		while (nodeNum != 1)
		{
			cin >> v >> p;
			myList->insert(v, p);
			--nodeNum;
		}
		cin >> delNode;
		myList->remove(delNode);
		myList->printList();
	}
}
說明
  1. 其實測試樣例不會在未定義的節點之後插入資料,也不會刪除沒有定義的節點,所以其實可以簡化一部分程式碼

  2. 本地IDE測試是沒有問題的,但是牛客網的測試環境輸出會異常。本地除錯的輸出結果和可以AC的程式碼在測試樣例上結果一致,但牛客網環境下會把輸入先輸出在輸出結果。

使用STL的簡潔版本
#include<iostream>
#include<algorithm>
#include<list>
int main()
{
	int nodeNum, headVal, delNodeVal, pos, val;
	std::cin >> nodeNum >> headVal;
	std::list<int> myList;
	myList.emplace_back(headVal);
	for (int i = 1; i < nodeNum; ++i)
	{
		std::cin >> val >> pos;
		myList.insert(++find(myList.begin(), myList.end(), pos), val);
	}
	std::cin >> delNodeVal;
	myList.erase(find(myList.begin(), myList.end(), delNodeVal));
	for (auto elem : myList)
		std::cout << elem << " ";
	std::cout << std::endl;
}
破案

需要處理多組測試資料,也就是說輸入都要用while()迴圈,Bcompare對了半天找bug,emmm

輸出單向連結串列中倒數第k個結點

問題描述詳見輸出單向連結串列中倒數第k個結點

雙指標的問題,單鏈表傳送門。這個issue有個需要注意的地方就是,最後一個節點是尾指標,也即當k允許取零,這時候取出的元素值是無效值,若不處理這種情況會提示越界訪問。

#include<iostream>
struct Node {
	int elem;
	Node* next;
	Node(int e) :elem(e), next(nullptr) {}
};

class linkList {
private:
	Node* head;  //elem儲存連結串列元素個數
public:
	linkList() {
		Node* node = new Node(0);
		head = node;
	}
	void insert(int num) {
		Node* it = head;
		int val;
		while (num--)
		{
			std::cin >> val;
			Node* node = new Node(val);
			it->next = node;
			it = it->next;
			++head->elem;
		}
	}
	Node* reverseK(int k) {
		if (k > head->elem)
			return nullptr;
		else
		{
			Node* p = head;
			Node* q = head;
			while (k--)
				q = q->next;
			while (q)
			{
				p = p->next;
				q = q->next;
			}
			std::cout << p->elem << std::endl;
			return p;
		}
	}
};
int main()
{
	int nodeNum, k;
	while (std::cin >> nodeNum) {
		linkList myList;
		myList.insert(nodeNum);
		std::cin >> k;
		if (k == 0)
			std::cout << 0 << std::endl;
		else
		myList.reverseK(k);
	}
}

密碼擷取

問題描述詳見字串運用-密碼擷取

遞迴

當字串很短時,可以考慮遞迴,但是當字串很長的時候效率很低。

//擷取最長對稱串
#include<iostream>
#include<string>
#include<algorithm>
auto getMax = [](int a, int b, int c) {
	return std::max(std::max(a, b), std::max(a, c));
};
bool check(std::string str)
{
	int len = str.length();
	for (int i = 0; i < len / 2; i++)
		if (str[i] != str[len - 1 - i])
			return false;
	return true;
}
int f(std::string str) {
	if (check(str))
		return str.length();
	return getMax(f(str.substr(0, str.length() - 1)), f(str.substr(1, str.length())), f(str.substr(1, str.length() - 1)));
}

int main()
{
	std::string str;
	while (std::cin >> str)
		std::cout << f(str) << std::endl;
}

注:

遞迴的複雜度可以通過儲存中間結果或改變遞迴方向等技巧來降低,這裡挖個坑~~日後來填

動態規劃

這個題目可以轉化成求最長公共子串的問題,將原字串逆轉之後,求最長公共子串

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

int main() {
	string x,y;

	while (cin >> x) {
		y.resize(x.size());
		reverse_copy(x.begin(), x.end(), y.begin());
		int maxLen = 0;

		int** dp = new int* [x.length() + 1];
		for (unsigned int i = 0; i <= x.length(); ++i)
			dp[i] = new int[y.length() + 1];

		for (unsigned int i = 0; i <= x.length(); ++i)
			for (unsigned int j = 0; j <= y.length(); ++j) {
				if (i == 0 || j == 0 || x[i - 1] != y[j - 1]) 
					dp[i][j] = 0;
				else
				{
					dp[i][j] = dp[i - 1][j - 1] + 1;
					maxLen = max(maxLen, dp[i][j]);
				}
			}

		cout << maxLen << endl;
    
		for (unsigned int i = 0; i < y.length(); ++i)  //二維陣列的釋放
			delete[]dp[i];
		delete[] dp;
	}
}
補充

本題可以遍歷一遍,計算每一個位置為中心時奇數和偶數長度迴文串的長度,輸出最大值

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

int decrypt(string passwd)
{
	 int maxLen = 0;
	for (int i = 1; i < passwd.length(); ++i)
	{
	    int low, high;

		low = i - 1, high = i;
		while (low >= 0 && high < passwd.length() && passwd[low] == passwd[high]) {
			low--;
			high++;
		}
		maxLen = max(maxLen, high - low - 1);

		low = i - 1; high = i + 1;
		while (low >= 0 && high < passwd.length() && passwd[low] == passwd[high]) {
			low--;
			high++;
		}
		maxLen = max(maxLen, high - low - 1);
	}
	return maxLen;
}

int main() {
	string str;
	while (cin >> str)
		cout << decrypt(str) << endl;
}

簡單密碼

問題描述詳見簡單密碼破解

注意大寫字母類似‘’迴圈移位’‘,取餘

#include <iostream>
using namespace std;

string decrypt(string str)
{
	for (auto& ch : str)
	{
		if (ch >= 'A' && ch <= 'Z')
			ch = static_cast<char>(static_cast<int>((ch - 'A' + 1) % 26) + 'a');
		else if (ch >= 'a' && ch <= 'c')
			ch = '2';
		else if (ch >= 'd' && ch <= 'f')
			ch = '3';
		else if (ch >= 'g' && ch <= 'i')
			ch = '4';
		else if (ch >= 'j' && ch <= 'l')
			ch = '5';
		else if (ch >= 'm' && ch <= 'o')
			ch = '6';
		else if (ch >= 'p' && ch <= 's')
			ch = '7';
		else if (ch >= 't' && ch <= 'v')
			ch = '8';
		else if (ch >= 'w' && ch <= 'z')
			ch = '9';
	}
	return str;
}

int main() {
	string passwd;
	while(cin >> passwd)
	cout << decrypt(passwd) << endl;
}