C++之用鏈表實現大數的加減乘除


1.前言

實現大數的加減乘除是今年寒假C++的大作業,原本我是用字符串寫的,但是后來看作業要求要用鏈表實現,於是我不情不願的用鏈表的實現了一遍

2.Num類

2.1Node類

首先是內部的一個Node類用於建立單向鏈表,size用於計算大小方便Num對象之間做比較

	class Node
	{
	public:
		int val;
		Node*next;
		Node(int v, Node*n)
		{
			val = v;
			next = n;
		}
	};
	Node*head;
	int size;

2.2構造函數,賦值函數以及析構函數

首先是默認構造函數,其實可能不會使用到,但為了方便,還是寫了

	Num()
	{
		head = NULL;
		size = 0;
		D = false;
	}

然后是主要的一個構造函數,當遇到'.'的時候,我們用-100來作為標記

該鏈表為逆向構造的一個數,方便進行加減操作

	Num(const string&s)
	{
		D = false;
		head = NULL;
		size = 0;
		for (int i = 0; i < s.size(); i++)
		{
			if (s[i] == ','||s[i]=='-')
				continue;
			size++;
			if (s[i] == '.')
				head = new Node(-100, head);
			else
				head = new Node(s[i] - '0', head);
		}
	}

接下來是拷貝構造函數以及賦值函數,兩者基本是一樣的

值得注意的是,賦值函數要首先確認賦值對象是否為本身,若為本身,則返回

	Num(const Num& num)
	{
		D = num.D;
		size = num.size;
		head = NULL;
		Node*cur = num.head, *temp=head;
		while (cur)
		{
			if (!head)
			{
				head = new Node(cur->val, NULL);
				temp = head;
			}
			else
			{
				temp->next = new Node(cur->val, NULL);
				temp = temp->next;
			}
			cur = cur->next;
		}
	}
	Num& operator=(const Num&num)
	{
		if (this == &num)
			return *this;
		free();
		size = num.size;
		head = NULL;
		D = num.D;
		Node*cur = num.head, *temp=head;
		while (cur)
		{
			if (!head)
			{
				head = new Node(cur->val, NULL);
				temp = head;
			}
			else
			{
				temp->next = new Node(cur->val, NULL);
				temp = temp->next;
			}
			cur = cur->next;
		}
		return *this;
	}

最后是析構函數,我們析構函數只需要回收掉鏈表的內存即可

為了方便函數內部回收鏈表內存,將回收操作寫為free函數

	~Num()
	{
		free();
	}
	void free()
	{
		size = 0;
		while (head)
		{
			Node*temp = head->next;
			delete head;
			head = temp;
		}
		head = NULL;
	}

上述即為Num類的基本組成函數

2.3加法操作與減法操作

為方便操作,加減時大數在前,小數在后,直接對本身對象進行操作,所以使用+=,-=以及返回對象本身引用

加減操作沒什么好說的,從最低位開始依次相加

在進位或借位時進行了一個判斷是否為小數點的操作,以防操作錯誤

	Num& operator+=(const Num&num)
	{
		Node*pa = head, *pb = num.head;
		while (pa||pb)
		{
			int b = 0;
			if (pb)
			{
				b = pb->val;
				pb = pb->next;
			}
			if (pa->val != -100)
			{
				pa->val += b;
				if (pa->val > 9)
				{
					if (pa->next)
					{
						if (pa->next->val == -100)
							pa->next->next->val++;
						else
							pa->next->val++;
					}
					else
					{
						pa->next = new Node(1, NULL);
						size++;
					}
					pa->val -= 10;
				}
			}
			pa = pa->next;
		}
		return *this;
	}
	Num& operator-=(const Num&num)
	{
		Node*pa = head, *pb = num.head;
		while (pa || pb)
		{
			int b = 0;
			if (pb)
			{
				b = pb->val;
				pb = pb->next;
			}
			if (pa->val != -100)
			{
				pa->val -= b;
				if (pa->val < 0)
				{
					if (pa->next->val == -100)
						pa->next->next->val--;
					else
						pa->next->val--;
					pa->val += 10;
				}
			}
			pa = pa->next;
		}
		return *this;
	}

2.4乘法操作

乘法操作其實和列式計算一樣,涉及到進位,但很容易理解

	Num operator*(const Num&num)
	{
		Num res(string(size + num.size, '0'));
		Node *pr = res.head, *temp;
		for (Node*pa = head; pa != NULL; pa = pa->next)
		{
			int carry = 0;
			temp = pr;
			for (Node*pb = num.head; pb != NULL; pb = pb->next,pr=pr->next)
			{
				int temp = pa->val*pb->val + carry + pr->val;
				pr->val = temp % 10;
				carry = temp / 10;
			}
			pr->val += carry;
			pr = temp->next;
		}
		return res;
	}

2.5除法操作

除法操作我們使用被除數減去除數乘以若個干10進行

比如58除以5 首先用58-50=8 然后商加10 然后用8-5=3 商加1 由於3比5小,就不進行操作了 最終結果為11

但是題目要求除法操作要保留十位小數四舍五入,我們只需要事先對被除數乘以10^11即可,最終再加上小數點進行四舍五入操作

我們首先實現了兩個與10有關的函數

	void Mul10(const int& x)
	{
		for (int i = 0; i < x; ++i)
		{
			head = new Node(0, head);
			size++;
		}
	}
	void Div10(const int&x)
	{
		for (int i = 0; i < x; ++i)
		{
			Node*temp = head->next;
			delete head;
			head = temp;
			size--;
		}
	}

然后是除法操作,res用於返回結果,p用於與res相加

d用於與被除數相減

	Num operator/(const Num&num)
	{
		Num res(string(1, '0')), p(string(1, '1'));
		res.D = true;
		p.D = true;
		Num d = num;
		while (num<=*this)
		{
			int len = size - num.size;
			d.Mul10(len);
			p.Mul10(len);
			int z = 0;
			if (*this < d)
			{
				d.Div10(1);
				p.Div10(1);
				z = -1;
			}
			if (res.head->val == 0&&res.size==1)
				res = p;
			else
				res += p;
			*this -= d;
			balance();
			d.Div10(len + z);
			p.Div10(len + z);
		}
		return res;
	}

我們注意到了除法操作中有比較<的操作,接下來便是重載<函數

暫時寫成單向鏈表轉化為字符串string比較的方法

	bool operator<(const Num&num)const
	{
		if (size < num.size)
			return true;
		else if (size > num.size)
			return false;
		string A(size, '0'), B(num.size, '0');
		Node*temp = head;
		for (int i = size - 1; i >= 0; --i)
		{
			if (temp->val == -100)
				A[i] = '.';
			else
				A[i] = temp->val + '0';
			temp = temp->next;
		}
		temp = num.head;
		for (int i = num.size - 1; i >= 0; --i)
		{
			if (temp->val == -100)
				B[i] = '.';
			else
				B[i] = temp->val + '0';
			temp = temp->next;
		}
		return cmp(A, B);
	}
	bool cmp(const string&a, const string&b)const
	{
		if (a.size() < b.size())
			return true;
		else if (a.size() > b.size())
			return false;
		else
			return a < b;
	}
	bool operator==(const Num&num)const
	{
		if (size != num.size)
			return false;
		Node*pa = head, *pb = num.head;
		while (pa&&pb)
		{
			if (pa->val != pb->val)
				return false;
			pa = pa->next;
			pb = pb->next;
		}
		return true;
	}
	bool operator<=(const Num&num)const
	{
		return *this == num || *this < num;
	}

2.6打印數字

我們用一個bool值D來標記是否為除法結果,若D為真,則為商,需要進行小數點處理

我們需要考慮下列情況
1.若為小數點,將小數后面0清空,若全為0,則去掉小數點
2.將前導0去掉,若只剩小數點,則加0,或全為0,則為0

	friend ostream& operator<<(ostream&os, const Num&num)
	{
		bool z = false;
		Num::Node* temp = num.head;
		string s(num.size, '0');
		for (int i = num.size - 1; i >= 0; --i)
		{
			if (temp->val == -100)
			{
				s[i] = '.';
				z = true;
			}
			else
				s[i] = temp->val + '0';
			temp = temp->next;
		}
		if (num.D)
		{
			if (s.size() <= 11)
				s = "0" + string(11 - s.size(), '0') + s;
			if (s[s.size() - 1] > '4')
				s[s.size() - 2]++;
			s.erase(s.end() - 1);
			for (int j = s.size() - 1; j > 0; --j)
			{
				if (s[j] > '9')
				{
					s[j - 1]++;
					s[j] -= 10;
				}
				else
					break;
			}
			if (s[0] > '9')
			{
				s[0] -= 10;
				s = "1" + s;
			}
			s.insert(s.size() - 10, ".");
			z = true;
		}
		int n = 0;
		while (s[n] == '0')
			n++;
		if (n == s.size())
			s = "0";
		else
			s = s.substr(n);
		if (s[0] == '.')
			s = '0' + s;
		bool t = true;
		int q;
		for (q = 0; q < s.size(); ++q)
			if (s[q] == '.')
				break;
		int j = s.size() - 1;
		if (z)
		{
			while (j > q&&s[j] == '0')
				j--;
			if (j == q)
				j--;
		}
		for (int i = 0; i <= j; ++i)
		{
			if (s[i] == '.')
				t = false;
			if (t && (q - i) % 3 == 0 && i != 0)
				os << ",";
			os << s[i];
		}
		os << endl;
		return os;
	}

2.7操作前處理

加減法將小數點對其,除法則將除數小數點去掉並將被除數擴大相應的倍數

void convert_Float_Plus(string&a, string&b)
{
	int x = 0, y = 0;
	while (x<a.size() && a[x] != '.')
		x++;
	while (y<b.size() && b[y] != '.')
		y++;
	if (x != a.size() || y != b.size())
	{
		int a_point = a.size() - x - 1, b_point = b.size() - y - 1;
		if (a_point > b_point)
			b += string(a_point - b_point, '0');
		else if (a_point < b_point)
			a += string(b_point - a_point, '0');
		a[x] = '.';
		b[y] = '.';
	}
}
void convet_Float_Divide(string&a, string&b)
{
	int x, y;
	for (x = 0; x < a.size(); ++x)
		if (a[x] == '.')
			break;
	for (y = 0; y < b.size(); ++y)
		if (b[y] == '.')
			break;
	if (y < b.size())
	{
		if (x < a.size())
			for (int i = 0; i < b.size() - y - 1; ++i)
				swap(a[x + i], a[x + 1 + i]);
		else
			a = a + string(b.size() - y - 1, '0');
		b.erase(b.begin() + y);
		int n = 0;
		while (b[n] == '0')
			++n;
		b = b.substr(n);
	}
	for (x = 0; x < a.size(); ++x)
		if (a[x] == '.')
			break;
	if (x < a.size())
	{
		if (a.size() - x - 1 <11)
			a = a + string(11 - (a.size() - x - 1), '0');
		else
			a = a.substr(0, x + 12);
		a.erase(a.begin() + x);
	}
	else
		a = a + string(11, '0');
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM