数据结构学习(一)链表
链表
ADT
完善了小马哥提供的模板得到的ADT,有许多意义不明的函数,纯属实现功能练练手,不必纠结
/* 单链表的结点定义 */
template <class ElemType>
struct LinkNode
{
ElemType data;
LinkNode<ElemType> *next;
LinkNode(LinkNode<ElemType> *ptr = NULL) { next = ptr; } //构造函数1,用于构造头结点
LinkNode(const ElemType &item, LinkNode<ElemType> *ptr = NULL) //构造函数2,用于构造其他结点
//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
{
next = ptr;
data = item;
}
ElemType getData() { return data; } //取得结点中的数据
void SetLink(LinkNode<ElemType> *link) { next = link; } //修改结点的next域
void SetData(ElemType value) { data = value; } //修改结点的data值
};
template <class ElemType> //带头结点的单链表
class LinkList
{
private:
LinkNode<ElemType> *head; // 头结点
LinkNode<ElemType> *tail; // 尾结点
int lenth; // 链表长度(不计头结点)
public:
LinkList() //无参数的构造函数
{
head = new LinkNode<ElemType>;
lenth = 0;
tail = head;
}
LinkList(const ElemType &item) //带参数的构造函数 -> 参数给头结点是否有点特殊?
{
head = new LinkNode<ElemType>(item);
lenth = 0;
tail = head;
}
LinkList(LinkList<ElemType> &List) //拷贝构造函数
{
head = new LinkNode<ElemType>;
lenth = List.ListLength();
LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
while (p1 != NULL)
{
*p3 = new LinkNode<ElemType>(*p1.data);
*p2.SetLink(p3);
p2 = p3;
p1 = p1->next;
}
tail = p2;
}
~LinkList() { ListDestroy(); } //析构函数
LinkList<ElemType> &operator=(LinkList<ElemType> &List) //重载函数:赋值
{
ListClear();
LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
while (p1 != NULL)
{
p3 = new LinkNode<ElemType>(p1->data);
p2->SetLink(p3);
p2 = p3;
p1 = p1->next;
}
lenth = List.ListLength();
tail = p2;
}
void ListDestroy() //销毁链表
{
LinkNode<ElemType> *p1 = head, *p2;
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
}
void ListClear() //清空链表
{
lenth = 0;
LinkNode<ElemType> *p1 = head->next, *p2;
head->SetLink(NULL);
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
tail = head;
}
int ListLength() const { return lenth; } //返回链表的长度
bool ListEmpty() const //判断链表是否为空表
{
if (lenth)
return 1;
else
return 0;
}
bool InsFirst(ElemType e) //在首节点之前插入一个结点
{
lenth++;
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
p2->SetLink(head->next);
head->SetLink(p2);
return 1;
}
LinkNode<ElemType> *GetHead() const { return head; } //获取链表头结点
void SetHead(LinkNode<ElemType> *p) { head = p; } //设置链表头结点
void SetLenth(int l) { lenth=l; } //设置长度
void SetTail(LinkNode<ElemType> *p) { tail = p; } //设置表尾节点
LinkNode<ElemType> *GetTail() { return tail; } //获取链表尾结点
bool ListInsert_prior(int pos, ElemType e) //在链表的第pos个位置之前插入e元素,可插在最末尾,不可覆盖头结点
{
if (pos <= 0 || pos > lenth + 1)
return 0;
lenth--;
LinkNode<ElemType> *p1 = head;
if (pos == lenth + 1)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
tail->SetLink(p2);
tail = p2;
return 1;
}
for (int i = 1; i <= pos - 1; i++)
{
p1 = p1->next;
if (i == pos - 1)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
p2->SetLink(p1->next);
p1->next = p2;
return 1;
}
}
}
bool ListInsert_next(int pos, ElemType e) //在链表的第pos个位置之后插入e元素,可插在最末尾
{
if (pos < 0 || pos > lenth)
return 0;
lenth++;
LinkNode<ElemType> *p1 = head;
if (pos == lenth)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
tail->SetLink(p2);
tail = p2;
return 1;
}
for (int i = 1; i <= pos; i++)
{
p1 = p1->next;
if (i == pos)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
p2->SetLink(p1->next);
p1->next = p2;
return 1;
}
}
}
//删除链表的首结点(注意不是头结点)
bool DelFirst()
{
if (lenth == 0)
return 0;
if (lenth == 1)
tail = head; //维护尾指针
lenth--;
LinkNode<ElemType> *p1 = head->next;
head->next = p1->next;
delete p1;
return 1;
}
//表头插入法动态生成链表 -> 和首节点插入元素有什么区别么= = -> 啊这个好像是数组!
void CreateList_Head(int n, ElemType *A)
{
lenth += n;
LinkNode<ElemType> *p1;
for (int i = n - 1; i >= 0; i--)
{
p1 = new LinkNode<ElemType>(A[i]);
p1->SetLink(head->next);
head->SetLink(p1);
}
}
//表尾插入法动态生成链表
void CreateList_Tail(int n, ElemType *A)
{
lenth += n;
LinkNode<ElemType> *p1;
for (int i = 0; i < n; i++)
{
p1 = new LinkNode<ElemType>(A[i]);
tail->SetLink(p1);
tail = p1;
}
}
//删除链表的第pos个位置的元素 -> 返回被删除的数据的话pos不合法怎么办呢?-> 返回空好了
ElemType ListDelete(int pos)
{
ElemType tmp;
if (pos <= 0 || pos > lenth)
return tmp;
lenth--;
LinkNode<ElemType> *p1 = head, *p2;
for (int i = 1; i <= pos; i++)
{
p2 = p1;
p1 = p1->next;
if (i == pos)
{
if(p1 == tail){ //维护尾指针
tail=p2;
}
p2->SetLink(p1->next);
tmp = p1->data;
delete p1;
lenth--; //维护长度
return tmp;
}
}
}
LinkNode<ElemType> *GetElem(int pos) const //用e返回链表的第i个元素的地址 -> pos不合法返回什么呢? -> 空吧!
{
LinkNode<ElemType> *tmp;
if (pos <= 0 || pos > lenth)
return tmp;
LinkNode<ElemType> *p1 = head;
for (int i = 1; i <= pos; i++)
{
p1 = p1->next;
if (i == pos)
return p1;
}
return p1;
}
//按值查找,即定位。从链表的第一个结点开始,判断当前结点值是否等于e。
//若是,则返回该结点的序号;否则继续后一个,直至表结束。找不到时返回0。
int SearchElem(const ElemType &e) const
{
int c = 1;
LinkNode<ElemType> *p1 = head->next;
while (p1 != NULL)
{
if (p1->data == e)
return c;
c++;
p1 = p1->next;
}
return 0;
}
//返回链表给定数据元素的前驱数据元素的值 -> 找不到或者为头结点就不改变咯
bool PriorElem(const ElemType cur_e, ElemType &pri_e)
{
LinkNode<ElemType> *p1 = head->next->next, *p2 = head->next; //忽略头结点的可能
while (p1 != NULL)
{
if (p1->data == cur_e)
{
pri_e = p2->data;
return 1;
}
}
return 0;
}
//遍历链表
bool ListTraverse() const
{
if (lenth == 0)
return 0;
LinkNode<ElemType> *p1 = head;
for (int i = 0;p1->next!=NULL; i++) //不以lenth为标准
{
p1 = p1->next;
if (i)
cout << "->";
cout << p1->data;
}
return 1;
}
void add(ElemType e)
{ //表尾添加节点
LinkNode<ElemType> *p1 = new LinkNode<ElemType>(e);
tail->SetLink(p1);
tail = p1;
lenth++;
}
};
链表的另一种实现(半封装)
ListNode *createByTail(){ //新建无头结点链表并从尾部插入
ListNode *head;
ListNode *p1, *p2;
int n = 0;
char num;
int len;
cin >> len; //输入长度
head = NULL;
while (n < len && cin >> num){ //生成链表
p1 = new ListNode(num);
n = n + 1;
if (n == 1)
head = p1; //头指针指向首结点
else
p2->next = p1;
p2 = p1;
}
return head; //返回新建链表的头结点
}
void displayLink(ListNode *head){ //格式化输出链表
ListNode *p;
p=head;
cout<<"head-->";
while(p!= NULL){
cout<<p->val<<"-->";
p=p->next;
}
cout<<"tail\n";
}
int main(){
ListNode *head = createByTail();
displayLink(head);
return 0;
}
这是链表的一种很有趣的实现,直接抛弃类封装,用头结点替代整个链表
参考:[
Fxy's CloudPan
]
功能函数(来自DHU数据结构OJ)
用字符串流实现整行不计数输入数据:
由于OJ中的数据输入不提前给出个数,直接整行读取,故需要利用字符串流实现整行读入:
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
int main(){
string s;
getline(cin,s); //getline会将最后的换行符从缓冲区中除掉
stringstream ss(s); //用s字符串初始化字符串流ss
int tmp;
while(ss>>tmp) //用字符串流ss给tmp输入数据,直到ss全都输入完毕
cout<<tmp<<" ";
return 1;
}
输入:
13 5 6 9 87 3 5 1 5 8 6 7 4 15 6 7
输出:
13 5 6 9 87 3 5 1 5 8 6 7 4 15 6 7
前m个元素和后n个元素的互换
先提供一段错误的代码片段:
//按序号查找,从链表的第一个结点开始,判断当前结点是否是第i个,
//若是,则给传入的指针赋值该节点地址;否则继续后一个,直至表结束。 没有第i个结点时不做修改。
bool FindElem(int k, LinkNode<ElemType> *e) {
LinkNode<ElemType> *p1=head;
if(k <= 0 || k > lenth) return 0;
for(int i=1;i<=k;i++){
p1=p1->next;
if(i==k){
e=p1;
return 1;
}
}
return 1;
}
//......省略其他
//逆置函数主体
template<class ElemType>
void Exchange_L( LinkList<ElemType> &L, int m ){
LinkNode<ElemType> *p1,*p2,*head=L.GetHead(),*tail=L.GetTail();
p1=L.FindElem(m,p1); // 向p1传入m位置的结点地址 (*)
p2=p1->next; // 后n个结点的首结点
p1->next=NULL; //尾结点的下个元素地址为NULL
L.SetTail(p1); //p1为新的尾结点
p1=head->next; //原首结点
head->SetLink(p2); //新首结点
tail->SetLink(p1); //旧尾结点与原首结点相接
}
//.....省略main函数
*行会出现一个内存报错:"segmentation fault",这是为什么呢?我们传入了一个p1指针,并试图在函数内部修改了它的值,但在这里实际上是失败了
在FindElem
函数中指针"e"的地址与指针"p1"的地址成功同步,但Exchange_L
函数内的p1指针指向的地址没有发生任何变化。可以做如下修改简单测试一下:
bool FindElem(int k, LinkNode<ElemType> *e) {
LinkNode<ElemType> *p1=head;
if(k <= 0 || k > lenth) return 0;
for(int i=1;i<=k;i++){
p1=p1->next;
if(i==k){
e=p1;
cout<< p1->data<<endl;
cout<<p1<<" "<<e<<endl; //p1=0xe81d30 e=0xe81d30
return 1;
}
}
return 1;
}
// ...
template<class ElemType>
void Exchange_L( LinkList<ElemType> &L, int m ){
LinkNode<ElemType> *p1,*p2,*head=L.GetHead(),*tail=L.GetTail();
cout<<p1<<endl; //p1=0x200000000
p1=L.GetElem(m);
cout<<p1; //p1=0x200000000
p2=p1->next;
p1->next=NULL;
L.SetTail(p1);
p1=head->next;
head->SetLink(p2);
tail->SetLink(p1);
}
这里需要解释传入指针同步变化的原理:平常传入指针,在函数内修改的是指针所指向地址的值,而地址在整个程序是唯一的,所以主函数与分函数的变量变化会同步;但这里传入指针后试图改变的是指针所指向的地址,这是不可行的。就像传入一个普通变量,在目标函数中会新建另一个临时变量参与函数,所以被传入的普通变量是不会同步函数内的修改的,而指针也是如此,目标函数中新建了另一个指针变量存放传入的地址,若函数中这个地址发生变化,不会影响到被传入的指针。(引用也是同理)也就是说此处的FindElem
函数
是无法实现它的功能的(指向指针的指针应该可以解决这个问题)
正确打开方式
template <class ElemType>
void Exchange_L(LinkList<ElemType> &L, int m)
{
LinkNode<ElemType> *p1, *p2, *head = L.GetHead(), *tail = L.GetTail();
p1 = L.GetElem(m); // 获取m位置的结点地址
p2 = p1->next; // 后n个节点的首结点
p1->next = NULL; //尾结点的下个元素地址为NULL
L.SetTail(p1); // p1为新的尾结点
p1 = head->next; //原首结点
head->SetLink(p2); //新首结点
tail->SetLink(p1); //旧尾结点与原首结点相接
}
单链表的连接
两个链表的归并操作需要注意修改:A的长度,A的尾指针,B的清空,B的长度,B的尾结点
template<class ElemType>
void Linklist_Contact( LinkList<ElemType> &A, LinkList<ElemType> &B ){
LinkNode<ElemType> *p1,*p2;
A.GetTail()->next=B.GetHead()->next;
A.SetTail(B.GetTail()); //A链表的尾指针
A.SetLenth(A.ListLength()+B.ListLength()); //设置A链表的长度
B.GetHead()->next=NULL; //清空B链表 (*)
B.SetLenth(0); //B链表的长度
B.SetTail(B.GetHead()); //B链表的尾结点
}
(*)行必须清空B链表,否则析构函数删除两次会导致内存再次出错(Delete p1
后"p1"会变成NULL,但其他地方指向该地址的指针内容不会变)
2个单链表的交叉归并
template<class ElemType>
void Merge_L( LinkList<ElemType> &A, LinkList<ElemType> &B ){
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next,*p3;
if(p1==NULL) return; //防止出现"NULL->next"的情况
p3=p1->next;
p1->SetLink(p2);
p1=p3;
while(p1!=NULL && p2->next!=NULL){
p3=p2->next;
p2->SetLink(p1);
p2=p3;
p3=p1->next;
p1->SetLink(p2);
p1=p3;
}
if(p2->next==NULL){
p2->SetLink(p1); //这里考虑了两个链表长度不一致的情况
}
B.GetHead()->SetLink(NULL); //B的清空
A.SetLenth(A.ListLength()+B.ListLength()); //A的长度
B.SetLenth(0); //B的长度
//这里省略了A的尾指针,太麻烦了就偷懒了qwq
}
有序单链表的归并(非纯)
要求:AB两链表均为非递减有序表,元素有重复,归并后仍为非递减有序
template <class ElemType>
void Merge_L_Order(LinkList<ElemType> &A, LinkList<ElemType> &B)
{
LinkNode<ElemType> *p1 = A.GetHead()->next, *p2 = B.GetHead()->next, *p3;
if (p1 == NULL)
{
A.GetHead()->SetLink(p2);
B.GetHead()->SetLink(NULL); //归并处理
A.SetLenth(B.ListLength());
B.SetLenth(0);
A.SetTail(B.GetTail());
B.SetTail(B.GetHead());
return;
}
if (p2 == NULL)
{
return;
}
if (p1->data < p2->data)
{
A.GetHead()->SetLink(p1);
p3 = p1;
p1 = p1->next;
}
else
{
A.GetHead()->SetLink(p2);
p3 = p2;
p2 = p2->next;
}
while (p1 != NULL && p2 != NULL)
{
if (p1->data < p2->data)
{
p3->SetLink(p1);
p3 = p1;
p1 = p1->next;
}
else
{
p3->SetLink(p2);
p3 = p2;
p2 = p2->next;
}
}
if (p1 == NULL) //循环结束必定有一条链表还需处理
{
p3->SetLink(p2);
A.SetTail(B.GetTail()); //A的尾结点
}
else
{
p3->SetLink(p1);
}
B.GetHead()->SetLink(NULL); //B的清空
A.SetLenth(A.ListLength() + B.ListLength()); //A的长度
B.SetLenth(0); //B的长度
B.SetTail(B.GetHead()); //B的尾结点
return;
}
有序单链表的提纯
template<class ElemType>
void Purge_Lk_OL( LinkList<ElemType> &A ){
LinkNode<ElemType> *p1=A.GetHead()->next,*p2,*p3;
while(p1!=NULL){
p2=p1->next;
p3=p1;
while(p2!=NULL){
if(p2->data==p1->data){
p3->SetLink(p2->next);
delete p2;
p2=p3->next;
A.SetLenth(A.ListLength()-1); //A的长度
} else {
p3=p2;
p2=p2->next;
}
}
if(p1->next==NULL) A.SetTail(p1); //A的尾结点
p1=p1->next;
}
}
长整数加法运算(不使用单链表存储计算结果)
这题真是给我做麻了,属于是六七道题当一题做,一坨函数结合一起实现一个牵强的功能,突出一个抽象
先往ADT中添加一个新函数用于逆置:
void ListReverse(){ //逆置
if(lenth<=1) return;
LinkNode<ElemType> *p1=head->next,*p2=p1->next,*p3; //前中后三个指针
p1->SetLink(NULL); //新的尾结点
A.SetTail(p1); //重置尾指针
while(p2->next!=NULL){ //p2指向p1后整体后移,p3记录p2后一个节点
p3=p2->next;
p2->SetLink(p1);
p1=p2;
p2=p3;
}
p2->SetLink(p1);
head->SetLink(p2); //新的头结点
return;
}
再是几个重要函数与主函数:
int string2int(string s){ //字符串转数字
int k1=s.length();
int c=0,f=1;
for(int i=0;i<k1;i++){
c+=f*(s[k1-1-i]-48);
f*=10;
}
return c;
}
string Int_String( int result ){ //数字转字符串,由于锁死四位,不足要往左补0
string s;
while(result){
s=char(result%10+48)+s;
result/=10;
}
int k1=s.length();
while(k1<4){
s='0'+s;
k1++;
}
return s;
}
template<class ElemType>
int Two_LongNum_Compare( LinkList<ElemType> &A, LinkList<ElemType> &B, const int &len_A, const int &len_B ){ //绝对值比大小
if(len_A>len_B) return 1; //长度比较
if(len_B>len_A) return 2;
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next;
while(p1!=NULL){
if(p1->data>p2->data) return 1; //同步高位比大小
if(p1->data<p2->data) return 2;
p1=p1->next;
p2=p2->next;
}
return 0;
}
template<class ElemType>
void Input_Int_Division( LinkList<ElemType> &L, string &str, int &length ){ //分成四位一组
int k1=str.length(); //取出长度
if(str[0]=='-'){ //判断正负
k1--; //减去负号的长度
L.GetHead()->SetData(-1);
int cnt=1,res=k1%4;
if(res) //如果为0就不要加项
L.add(string2int(str.substr(1,res)));
for(int i=0;i<(k1-res)/4;i++){
cnt++;
L.add(string2int(str.substr(res+1+4*i,4))); //substr(string,pos,lenth)
}
length=cnt; //块的数量
} else {
L.GetHead()->SetData(1);
int cnt=1,res=k1%4;
if(res) //如果为0就不要加项
L.add(string2int(str.substr(0,res))); //前面非四个整的部分
for(int i=0;i<(k1-res)/4;i++){
cnt++;
L.add(string2int(str.substr(res+4*i,4)));
}
length=cnt;
}
}
template<class ElemType>
void Long_Int_Add( LinkList<ElemType> &A, LinkList<ElemType> &B, string &result, const int &len_A, const int &len_B ){ //长整型相加
if(!(A.GetHead()->data+B.GetHead()->data)){ //异号减法
int tmp=Two_LongNum_Compare(A,B,len_A,len_B);
A.ListReverse();
B.ListReverse();
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next;
if(tmp==1){ //A>B
int f=0;
while(p1!=NULL && p2!=NULL){
int c=p1->data-p2->data+f; //减法
f=0;
if(c<0){ //借位
c+=10000;
f=-1;
}
result=Int_String(c)+result; //连接字符串
p1=p1->next; //迭代
p2=p2->next;
}
while(p1!=NULL){
int c=p1->data+f; //上一个f可能为-1
f=0;
result=Int_String(c)+result; //往左边连接,这一片不好判断是否是最后一组,也就无法精确控制最后一组不补0
p1=p1->next;
}
if(A.GetHead()->data==-1) result='-'+result;
}
if(tmp==2){ //B>A
int f=0;
while(p1!=NULL && p2!=NULL){ //同步
int c=p2->data-p1->data+f;
// cout<<p2->data<<" - "<<p1->data<<" = "<<c<<endl; //检测中间过程
f=0;
if(c<0){
c+=10000;
f=-1; //借位
}
result=result+Int_String(c); //往左边连接
// cout<<result<<endl;
p1=p1->next;
p2=p2->next;
}
while(p2!=NULL){ //长出来的部分
int c=p1->data+f; //之前还留有的借位
f=0;
result=result+Int_String(c);
p2=p2->next;
}
if(B.GetHead()->data==-1) result='-'+result;
}
if(Two_LongNum_Compare(A,B,len_A,len_B)==0){ //B=A
result=result+'0';
}
} else { //同号
A.ListReverse(); //反转
B.ListReverse();
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next; //反转后才能取首结点,表示从低位开始运算
int f=0;
while(p1!=NULL && p2!=NULL){
int c=p2->data+p1->data+f;
f=c/10000; //进位
c%=10000;
result=Int_String(c)+result; //连接字符串
p1=p1->next; //迭代
p2=p2->next;
}
while(p1!=NULL){ //如果A更长
int c=p1->data+f;
f=0;
result=Int_String(c)+result;
p1=p1->next;
}
while(p2!=NULL){ //如果B更长
int c=p2->data+f;
f=0;
result=Int_String(c)+result;
p2=p2->next;
}
if(f){ //如果一样长但进位了
result=Int_String(f)+result;
}
if(B.GetHead()->data==-1) result='-'+result; //如果为负
}
}
template<class ElemType>
bool ListTraverse(LinkList<ElemType> &LInt){ //格式化遍历输出,注意除了第一节,后面的不足四位要往左边补0
LinkNode<ElemType> *p1=LInt.GetHead();
if(p1->data==-1) cout<<"-";
p1=p1->next;
int c=0;
while(p1!=NULL){ //cout补0是真的麻烦啊,所以可以利用Int_string转化补0,不需要补0的直接输出数字即可
if(c) cout<<",";
if(!c)
cout << p1->data;
else
cout << Int_String(p1->data);
p1=p1->next;
c++;
}
cout<<endl;
return 1;
}
int main(){
string s,s_,sum;
LinkList<int> L1,L2,L3;
int l1,l2,l3;
getline(cin,s);
getline(cin,s_);
Input_Int_Division(L1,s,l1);
ListTraverse(L1);
Input_Int_Division(L2,s_,l2);
ListTraverse(L2);
Long_Int_Add(L1,L2,sum,l1,l2);
cout<<endl;
Input_Int_Division(L3,sum,l3); //将字符串结果重新封装进链表中,再格式化输出
ListTraverse(L3);
return 0;
}
链表存储结果版本:
//.....
template<class ElemType>
void Long_Int_Add( LinkList<ElemType> &A, LinkList<ElemType> &B, LinkList<ElemType> &C, const int &len_A, const int &len_B ){ //长整型相加
if(!(A.GetHead()->data+B.GetHead()->data)){ //异号减法
int tmp=Two_LongNum_Compare(A,B,len_A,len_B);
A.ListReverse();
B.ListReverse();
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next;
if(tmp==1){ //A>B
int f=0;
while(p1!=NULL && p2!=NULL){
int c=p1->data-p2->data+f; //减法
f=0;
if(c<0){ //借位
c+=10000;
f=-1;
}
C.add(c); //连接字符串
p1=p1->next; //迭代
p2=p2->next;
}
while(p1!=NULL){
int c=p1->data+f; //上一个f可能为-1
f=0;
C.add(c); //直接推入链表中,add为从尾巴添加,最后结果需要逆置
p1=p1->next;
}
if(A.GetHead()->data==-1) C.GetHead()->SetData(-1);
else C.GetHead()->SetData(1); //设置符号位
}
if(tmp==2){ //B>A
int f=0;
while(p1!=NULL && p2!=NULL){ //同步
int c=p2->data-p1->data+f;
// cout<<p2->data<<" - "<<p1->data<<" = "<<c<<endl; //检测中间过程
f=0;
if(c<0){
c+=10000;
f=-1; //借位
}
C.add(c); //添入链表
// cout<<result<<endl;
p1=p1->next;
p2=p2->next;
}
while(p2!=NULL){ //长出来的部分
int c=p1->data+f; //之前还留有的借位
f=0;
C.add(c);
p2=p2->next;
}
if(B.GetHead()->data==-1) C.GetHead()->SetData(-1);
else C.GetHead()->SetData(1);
}
if(Two_LongNum_Compare(A,B,len_A,len_B)==0){ //B=A
C.add(0);
}
C.ListReverse();
} else { //同号
A.ListReverse(); //反转
B.ListReverse();
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next; //反转后才能取首结点,表示从低位开始运算
int f=0;
while(p1!=NULL && p2!=NULL){
int c=p2->data+p1->data+f;
f=c/10000; //进位
c%=10000;
C.add(c); //连接字符串
p1=p1->next; //迭代
p2=p2->next;
}
while(p1!=NULL){ //如果A更长
int c=p1->data+f;
f=0;
C.add(c);
p1=p1->next;
}
while(p2!=NULL){ //如果B更长
int c=p2->data+f;
f=0;
C.add(c);
p2=p2->next;
}
if(f){ //如果一样长但进位了
C.add(f);
}
if(B.GetHead()->data==-1) C.GetHead()->SetData(-1); //如果为负
else C.GetHead()->SetData(1);
C.ListReverse(); //逆置结果
}
}
int main(){
string s,s_,sum;
LinkList<int> L1,L2,L3;
int l1,l2,l3;
getline(cin,s);
getline(cin,s_);
Input_Int_Division(L1,s,l1);
ListTraverse(L1);
Input_Int_Division(L2,s_,l2);
ListTraverse(L2);
Long_Int_Add(L1,L2,L3,l1,l2);
cout<<endl;
ListTraverse(L3);
return 0;
}
只是简单修改了两个函数
参考:[
VSC自动换行
COUT格式化输出
STL_String用法
]
按要求提纯链表
要求:表中所有元素为数字,且绝对值小于输入值N,要求以尽可能低的时间复杂度保留绝对值相同的第一个出现的结点
int absolute(int a){ //取绝对值
if(a>=0) return a;
return -a;
}
template<class ElemType>
void Delete_Equal_Node( LinkList<ElemType> &A, int N ){ //数组存放数值来检测是否有重复结点,时间复杂度只有O(n)
int *a=new int[N+1];
memset(a,0,(N+1)*sizeof(int)); //数组置0
LinkNode<ElemType> *p1=A.GetHead()->next,*p2;
while(p1!=NULL){
int tmp=a[absolute(p1->data)]; //绝对值要加在p1值的外面!别包在数组外边了!
// cout<<tmp<<" "; //测试重复标记
if(tmp){ //检查绝对值是否已经出现过
p2->SetLink(p1->next); //删除结点
// cout<<"delete: "<<p1->data<<endl; //测试删除的结点
delete p1;
p1=p2->next;
A.SetLenth(A.ListLength()-1); //长度减一
} else {
a[absolute(p1->data)]=1; //标记已出现过的绝对值
p2=p1;
p1=p1->next;
}
}
A.SetTail(p2); //设置尾结点
}
这里因为绝对值包错地方导致出了点错,写了几个测试语句
有序单链表的归并(提纯)
要求:有链表AB均为非递减有序链表,两链表本身无重复,但相互可能有相同元素,要求使用原有的存储空间,归并到A链表提纯并保持有序
template<class ElemType>
void Merge_L_Order( LinkList<ElemType> &A, LinkList<ElemType> &B ){
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=B.GetHead()->next,*p3,*p4=A.GetHead();
while(p2!=NULL && p1!=NULL){
int t1=p1->data,t2=p2->data;
if(t1==t2){
p3=p2->next;
delete p2; //删除重复结点
// cout<<"delete: "<<t2<<endl; //测试删除情况
p2=p3; //p2推进
p4=p1;
p1=p1->next;
A.SetLenth(A.ListLength()-1); //设置长度
}
if(t1>t2){
// cout<<t2<<" linked"<<endl; //测试连接情况
p4->SetLink(p2);
p3=p2->next;
p4=p2; //更换用于连接的"前一结点",存于p4中,注意p4存的不一定是p1的原有前置结点
p2->SetLink(p1); //p2推进
p2=p3;
}
if(t1<t2){
p4=p1; //p1推进,p4跟进
p1=p1->next;
}
// A.ListTraverse(); //测试链表情况
// cout<<endl;
}
if(p1==NULL){ //B链有剩余
p4->SetLink(p2);
A.SetTail(B.GetTail()); //设置A的尾结点
}
A.SetLenth(A.ListLength()+B.ListLength()); //归并处理
B.GetHead()->SetLink(NULL);
B.SetLenth(0);
B.SetTail(B.GetHead());
}
本题注意更换用于连接的前置结点p4
逆置(外部)
template<class ElemType>
void Lk_Reverse( LinkList<ElemType> &A ){
if(A.ListLength()<=1) return;
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=p1->next,*p3; //前中后三个指针
p1->SetLink(NULL); //新的尾结点
A.SetTail(p1); //重置A的尾指针
while(p2->next!=NULL){ //p2指向p1后整体后移,p3记录p2后一个节点
p3=p2->next;
p2->SetLink(p1);
p1=p2;
p2=p3;
}
p2->SetLink(p1);
A.GetHead()->SetLink(p2); //新的首结点
return;
}
前面写过,直接套
最小结点提前
要求:将链表中最小的结点提前至首结点,不允许新建结点
template<class ElemType>
void Linklist_MoveMinToFirst( LinkList<ElemType> &A ){
if(A.ListLength()<=1) return;
LinkNode<ElemType> *p1=A.GetHead()->next,*p2=p1->next,*p3=p1,*p4;
while(p2!=NULL){
if(p2->data<p1->data){
p4=p3; //记录最小结点的前置结点
p1=p2; //记录最小结点
}
p3=p2; //记录遍历结点的前置结点
p2=p2->next; //遍历结点
}
p3=p1;
if(A.GetTail()==p3){ //恰好尾结点
A.SetTail(p4); //重置尾指针
p4->SetLink(NULL); //新的尾结点
p2=A.GetHead()->next; //最小结点提到首结点处
A.GetHead()->SetLink(p1);
p1->SetLink(p2);
}
if(A.GetHead()->next!=p3){ //中间结点
p4->SetLink(p1->next);
p2=A.GetHead()->next;
A.GetHead()->SetLink(p1);
p1->SetLink(p2);
}
return ;
}
一元多项式的加/减法运算
题目要求:输入0或1决定两个多项式相加或相减(上式减下式),第二三行分别输入第一个多项式的系数与指数,第三四行分别输入第二个多项式的系数与指数(默认系数指数一一对应),输出计算结果
重置ADT:
/* 多项式的结点ADT */
template <class ElemType1,class ElemType2>
struct LinkNode
{
ElemType1 factor; //系数
ElemType2 indice; //指数
LinkNode<ElemType1,ElemType2> *next;
LinkNode(LinkNode<ElemType1,ElemType2> *ptr = NULL) { next = ptr; } //构造函数1,用于构造头结点
LinkNode(const ElemType1 &item1, const ElemType2 &item2, LinkNode<ElemType1,ElemType2> *ptr = NULL) //构造函数2,用于构造其他结点
//函数参数表中的形参允许有默认值,但是带默认值的参数需要放后面
{
next = ptr;
factor = item1;
indice = item2;
}
ElemType1 getFactor() { return factor; } //取得系数
ElemType2 getIndice() { return indice; } //取得指数
void SetLink(LinkNode<ElemType1,ElemType2> *link) { next = link; } //修改结点的next域
void SetFactor(ElemType1 value) { factor = value; } //修改结点的系数
void SetIndice(ElemType2 value) { indice = value; } //修改结点的指数
};
template <class ElemType1,class ElemType2> //多项式的单链表ADT
class LinkList
{
private:
LinkNode<ElemType1,ElemType2> *head; // 头结点
LinkNode<ElemType1,ElemType2> *tail; // 尾结点
int lenth; // 链表长度(不计头结点)
public:
LinkList() //无参数的构造函数
{
head = new LinkNode<ElemType1,ElemType2>;
lenth = 0;
tail = head;
}
LinkList(const ElemType1 &item1,const ElemType2 &item2) //带参数的构造函数 -> 参数给头结点是否有点特殊?
{
head = new LinkNode<ElemType1,ElemType2>(item1,item2);
lenth = 0;
tail = head;
}
LinkList(LinkList<ElemType1,ElemType2> &List) //拷贝构造函数
{
head = new LinkNode<ElemType1,ElemType2>;
lenth = List.ListLength();
LinkNode<ElemType1,ElemType2> *p1 = List.GetHead()->next, *p2 = head, *p3;
while (*p1 != NULL)
{
*p3 = new LinkNode<ElemType1,ElemType2>(p1->getFactor(),p1->getIndice());
*p2.SetLink(p3);
p2 = p3;
p1 = p1->next;
}
tail = p2;
}
~LinkList() { ListDestroy(); } //析构函数
void ListDestroy() //销毁链表
{
LinkNode<ElemType1,ElemType2> *p1 = head, *p2;
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
}
void ListClear() //清空链表
{
lenth=0;
LinkNode<ElemType1,ElemType2> *p1 = head->next, *p2;
head->SetLink(NULL);
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
tail = head;
}
int ListLength() const { return lenth; } //返回链表的长度
bool ListEmpty() const //判断链表是否为空表
{
if (lenth)
return 1;
else
return 0;
}
LinkNode<ElemType1,ElemType2> *GetHead() const { return head; } //获取链表头结点
void SetHead(LinkNode<ElemType1,ElemType2> *p) { head = p; } //设置链表头结点
void SetLenth(int l) {lenth=l;}
void SetTail(LinkNode<ElemType1,ElemType2> *p) { tail = p; } //设置表尾节点
LinkNode<ElemType1,ElemType2> *GetTail() { return tail; } //获取链表尾结点
//删除链表的第pos个位置的元素 -> 返回被删除的数据的话pos不合法怎么办呢?-> 返回空好了
void ListDelete(int pos){
if(pos<=0 || pos>lenth) return;
lenth--;
LinkNode<ElemType1,ElemType2> *p1=head,*p2;
for(int i=1;i<=pos;i++){
p2=p1;
p1=p1->next;
if(i==pos){
p2->SetLink(p1->next);
delete p1;
return ;
}
}
}
LinkNode<ElemType1,ElemType2>* GetElem(int pos) const //用e返回链表的第i个元素的地址 -> pos不合法返回什么呢? -> 空吧!
{
LinkNode<ElemType1,ElemType2> *tmp;
if(pos <=0 || pos >lenth) return tmp;
LinkNode<ElemType1,ElemType2> *p1 = head;
for (int i = 1; i <= pos; i++)
{
p1 = p1->next;
if (i == pos)
return p1;
}
return p1;
}
//遍历链表
bool ListTraverse() const{
if(lenth==0){ //默认没有系数为0的结点,则长度为0即结果为0
cout<<0;
return 0;
}
LinkNode<ElemType1,ElemType2> *p1=head;
for(int i=0;p1->next!=NULL;i++){
p1=p1->next;
if(p1->getFactor()<0){ //系数为负
if(p1->getIndice()==0) cout<<p1->getFactor(); //指数为0不输出x
if(p1->getIndice()==1) { //指数为1不输出次数,懒得考虑负指数= =
if(p1->getFactor()==-1) //系数为-1时别输出!
cout<<"-"<<"x";
else
cout<<p1->getFactor()<<"x";
}
if(p1->getIndice()>1) { //不用额外加负号,指数自带
if(p1->getFactor()==-1)
cout<<"-x^"<<p1->getIndice();
else
cout<<p1->getFactor()<<"x^"<<p1->getIndice();
}
}
else { //系数为正
if(i) cout<<"+"; //判断是否输出加号
if(p1->getIndice()==0) cout<<p1->getFactor();
if(p1->getIndice()==1) { //指数为1不输出次数
if(p1->getFactor()==1) //系数为1不用输出
cout<<"x";
else
cout<<p1->getFactor()<<"x";
}
if(p1->getIndice()>1) { //不用额外加负号,指数自带
if(p1->getFactor()==1)
cout<<"x^"<<p1->getIndice();
else
cout<<p1->getFactor()<<"x^"<<p1->getIndice();
}
}
}
return 1;
}
void add(ElemType1 e1,ElemType2 e2){ //表尾添加节点
LinkNode<ElemType1,ElemType2> *p1=new LinkNode<ElemType1,ElemType2>(e1,e2);
tail->SetLink(p1);
tail=p1;
lenth++;
}
};
函数部分:
template<class ElemType1,class ElemType2>
void noZero(LinkList<ElemType1,ElemType2> &A){ //删除系数为0的结点方便输出
LinkNode<ElemType1,ElemType2> *p1=A.GetHead()->next,*p2=A.GetHead(); //p2记录前置节点方便删除
while(p1!=NULL){
if(p1->getFactor()==0){
p2->SetLink(p1->next);
delete p1;
p1=p2->next; //p2无需更新
A.SetLenth(A.ListLength()-1); //更新长度
} else {
p2=p1;
p1=p1->next;
}
}
A.SetTail(p2); //维护尾指针
}
template<class ElemType1,class ElemType2>
void Add_Poly( LinkList<ElemType1,ElemType2> &A, LinkList<ElemType1,ElemType2> &B, int add_sub){ //符号位add_sub传入+1或-1
LinkNode<ElemType1,ElemType2> *p1=A.GetHead()->next,*p2=B.GetHead()->next,*p3,*p4=A.GetHead(); //合并时清空B,删除或是插入结点
int f=add_sub; //符号位,标记加或者减
while(p1!=NULL && p2!=NULL){ //链表从头到尾指数逐渐变大
int k1=p1->getIndice(),k2=p2->getIndice(); //判断系数是否相同
if(k1==k2){
p1->SetFactor(p1->getFactor()+f*p2->getFactor()); //系数计算
p4=p1; //p4做前置结点,以防止需要插入结点
p1=p1->next;
p3=p2->next;
delete p2; //删除已经合并的结点
B.SetLenth(B.ListLength()-1); //更新B的长度
p2=p3;
}
if(k1<k2){
p4=p1; //前置结点转移,容易遗忘
p1=p1->next; //A的指数过小
}
if(k1>k2){ //B的指数过小,说明B有A中没有的指数,这里需要将这个结点插入A中
p4->SetLink(p2);
p4=p2; //p4指向前置结点
p3=p2->next;
p2->SetFactor(f*p2->getFactor()); //别忘了确认符号
p2->SetLink(p1);
p2=p3;
A.SetLenth(A.ListLength()+1); //更新A的长度
B.SetLenth(B.ListLength()-1); //更新B的长度
}
}
if(p2!=NULL){ //p2中还有结点
p4->SetLink(p2);
while(p2){
p2->SetFactor(p2->getFactor()*f); //确认符号
p2=p2->next;
}
A.SetTail(B.GetTail()); //重置A的尾指针
A.SetLenth(A.ListLength()+B.ListLength());
}
B.GetHead()->SetLink(NULL); //归并处理
B.SetLenth(0);
B.SetTail(B.GetHead());
return;
}
int main(){
int a;
cin>>a;
cin.get();
switch(a){
case 0:{
LinkList<double,int> Link1,Link2;
int b;
double c;
string s1,s2;
getline(cin,s1);
getline(cin,s2);
stringstream ss1(s1),ss2(s2);
while(ss1>>c){
ss2>>b;
Link1.add(c,b);
}
Link1.ListTraverse();
cout<<endl;
getline(cin,s1);
getline(cin,s2);
stringstream ss3(s1),ss4(s2);
while(ss3>>c){
ss4>>b;
Link2.add(c,b);
}
Link2.ListTraverse();
cout<<endl;
cout<<endl;
Add_Poly(Link1,Link2,1);
noZero(Link1);
Link1.ListTraverse();
break;
}
case 1:{
LinkList<double,int> Link1,Link2;
int b;
double c;
string s1,s2;
getline(cin,s1);
getline(cin,s2);
stringstream ss1(s1),ss2(s2);
while(ss1>>c){
ss2>>b;
Link1.add(c,b);
}
Link1.ListTraverse();
cout<<endl;
getline(cin,s1);
getline(cin,s2);
stringstream ss3(s1),ss4(s2);
while(ss3>>c){
ss4>>b;
Link2.add(c,b);
}
Link2.ListTraverse();
cout<<endl;
cout<<endl;
Add_Poly(Link1,Link2,-1);
noZero(Link1);
Link1.ListTraverse();
break;
}
default:{
cout<<"err";
return 0;
}
}
return 0;
}
整理一下这题的思路:首先要写一个适配于一元多项式的结点ADT和与之对应的链表ADT;然后要注意输入0,1的加减不同,无论第二个多项式的结点怎样插入第一个多项式都要判定符号!再者是特殊情况的特殊输出,如全无结果时输出0(这里利用noZero函数清除系数为0的结点),x指数为0时不带指数,系数为1时不带系数,第一项为正则不输出正号,某项为负则输出负号。
从解题的角度来讲,思路的全面是最重要的,其次要注意检查,也许你的思路全面了,但是实际编写代码的时候犯下了一些错误让你没能完全实现自己的想法,再不行啊,那看测试案例呗= =
循环链表的合并
循环单链表:尾结点指向头结点形成一个循环的单链表,要注意判定尾结点不能再用下一个结点是否为空,要用下一个节点是否为头结点;一定要有一个头结点,否则真的没头没尾会让很多函数变得很麻烦
循环单链表ADT:
/* 结点ADT无变化 */
template <class ElemType>
struct LinkNode
{
ElemType data;
LinkNode<ElemType> *next;
LinkNode(LinkNode<ElemType> *ptr = NULL) { next = ptr; }
LinkNode(const ElemType &item, LinkNode<ElemType> *ptr = NULL)
{
next = ptr;
data = item;
}
ElemType getData() { return data; }
void SetLink(LinkNode<ElemType> *link) { next = link; }
void SetData(ElemType value) { data = value; }
};
template <class ElemType> //循环单链表ADT,注意尾结点指向头结点和尾结点的判断条件即可
class CirLinkList
{
private:
LinkNode<ElemType> *head;
LinkNode<ElemType> *tail;
int lenth;
public:
CirLinkList() //无参数的构造函数
{
head = new LinkNode<ElemType>;
head->SetLink(head); //循环单链表为空表时头结点指向自己
lenth = 0;
tail = head;
}
CirLinkList(const ElemType &item)
{
head = new LinkNode<ElemType>(item);
head->SetLink(head); //循环单链表为空表时头结点指向自己
lenth = 0;
tail = head;
}
CirLinkList(CirLinkList<ElemType> &List) //拷贝构造函数
{
head = new LinkNode<ElemType>;
lenth = List.ListLength();
LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
while (p1->next != List->GetHead()) //用下一个结点是否是头结点来判定尾结点
{
*p3 = new LinkNode<ElemType>(*p1.data);
*p2.SetLink(p3);
p2 = p3;
p1 = p1->next;
}
tail = p2;
}
~CirLinkList() { ListDestroy(); } //析构函数
CirLinkList<ElemType> &operator=(CirLinkList<ElemType> &List) //重载函数:赋值
{
ListClear();
LinkNode<ElemType> *p1 = List.GetHead()->next, *p2 = head, *p3;
while (p1->next != List->GetHead())
{
p3 = new LinkNode<ElemType>(p1->data);
p2->SetLink(p3);
p2 = p3;
p1 = p1->next;
}
lenth = List.ListLength();
tail = p2;
}
void ListDestroy() //销毁链表
{
LinkNode<ElemType> *p1 = head, *p2;
tail->SetLink(NULL); //先拆散循环再逐一销毁
while (p1->next != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
}
void ListClear() //清空链表,保留头结点
{
lenth = 0;
LinkNode<ElemType> *p1 = head->next, *p2;
head->SetLink(head);
while (p1->next != head)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
tail = head;
}
int ListLength() const { return lenth; } //返回链表的长度
bool ListEmpty() const //判断链表是否为空表
{
if (lenth)
return 1;
else
return 0;
}
LinkNode<ElemType> *GetHead() const { return head; } //获取链表头结点
void SetHead(LinkNode<ElemType> *p) { head = p; } //设置链表头结点
void SetTail(LinkNode<ElemType> *p) { tail = p; } //设置表尾节点
void SetLenth(int l) { lenth = l; } //设置链表长度
LinkNode<ElemType> *GetTail() { return tail; } //获取链表尾结点
bool ListInsert_prior(int pos, ElemType e) //在链表的第pos个位置之前插入e元素,可插在最末尾,不可覆盖头结点
{
if (pos <= 0 || pos > lenth + 1)
return 0;
lenth--;
LinkNode<ElemType> *p1 = head;
if (pos == lenth + 1)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
tail->SetLink(p2);
tail = p2;
return 1;
}
for (int i = 1; i <= pos - 1; i++)
{
p1 = p1->next;
if (i == pos - 1)
{
LinkNode<ElemType> *p2 = new LinkNode<ElemType>(e);
p2->SetLink(p1->next);
p1->next = p2;
return 1;
}
}
}
//删除链表的首结点(注意不是头结点)
bool DelFirst(ElemType &e)
{
if (lenth == 0)
return 0;
LinkNode<ElemType> *p1 = head->next;
if (lenth == 1){
tail == head; //维护尾结点
head->SetLink(head); //循环链表
}
head->next = p1->next;
delete p1;
lenth--; //维护长度
return 1;
}
//删除链表的第pos个位置的元素 -> 返回被删除的数据的话pos不合法怎么办呢?-> 返回空好了
ElemType ListDelete(int pos)
{
ElemType tmp;
if (pos <= 0 || pos > lenth)
return tmp;
lenth--;
LinkNode<ElemType> *p1 = head, *p2;
for (int i = 1; i <= pos; i++)
{
p2 = p1;
p1 = p1->next;
if (i == pos)
{
if(p1 == tail){ //维护尾指针
tail=p2;
}
p2->SetLink(p1->next);
tmp = p1->data;
delete p1;
lenth--; //维护长度
return tmp;
}
}
}
LinkNode<ElemType> *GetElem(int pos) const //用e返回链表的第i个元素的地址 -> pos不合法返回什么呢? -> 空吧!
{
LinkNode<ElemType> *tmp;
if (pos <= 0 || pos > lenth)
return tmp;
LinkNode<ElemType> *p1 = head;
for (int i = 1; i <= pos; i++)
{
p1 = p1->next;
if (i == pos)
return p1;
}
return p1;
}
//遍历链表
bool ListTraverse() const
{
if (lenth == 0)
return 0;
LinkNode<ElemType> *p1 = head;
for (int i = 0;p1->next!=head; i++) //不以lenth为标准
{
p1 = p1->next;
if (i)
cout << "->";
cout << p1->data;
}
return 1;
}
void add(ElemType e)
{ //表尾添加节点
LinkNode<ElemType> *p1 = new LinkNode<ElemType>(e);
tail->SetLink(p1);
tail = p1;
p1->SetLink(head);
lenth++;
}
};
实现函数很简单,一半都是在维护链表:
template<class ElemType>
void Linklist_Contact( CirLinkList<ElemType> &A, LinkList<ElemType> &B ){
LinkNode<ElemType> *p1,*p2;
A.GetTail()->next=B.GetHead()->next;
A.SetTail(B.GetTail());
A.GetTail()->next=A.GetHead();
B.GetHead()->next=B.GetHead(); //链表的变量维护
B.SetTail(B.GetHead());
A.SetLenth(A.ListLength()+B.ListLength());
B.SetLenth(0);
}
约瑟夫环
题目要求:有n个人编号分别为1,2,3...n,他们各持有一个密码,有一个初始随机数m,编号为1的人开始以顺时针方向从1开始报数,数到m的人出圈,m变为出圈人的密码,并从顺时针方向的第一个人开始,再次从1开始报数...直到所有人都出圈,要求按顺序输出出圈者的编号
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
template <class ElemType> //为保持链表的统一少修改点东西,懒得删了= =,但其实没有什么用
struct LinkNode //约瑟夫结点ADT
{
int number; //排序
int code; //密码
LinkNode<ElemType> *next;
LinkNode(LinkNode<ElemType> *ptr = NULL) { next = ptr; }
LinkNode(int n,int e) { number = n;code = e; } //带参构造函数
ElemType getNumber() { return number; }
ElemType getCode() { return code; }
void SetLink(LinkNode<ElemType> *link) { next = link; } //修改next域
void SetNumber(int value) { number = value; } //设定排序
void SetCode(int value) { code = value; } //设定密码
};
template <class ElemType> //带头结点的单链表
class CirLinkList
{
private:
LinkNode<ElemType> *head; // 头结点
LinkNode<ElemType> *tail; // 尾结点
int lenth; // 链表长度(不计头结点)
public:
CirLinkList() //无参数的构造函数
{
head = new LinkNode<ElemType>;
head->SetLink(head); //循环单链表为空表时头结点指向自己
lenth = 0;
tail = head;
}
~CirLinkList() { ListDestroy(); } //析构函数
void ListDestroy() //销毁链表
{
LinkNode<ElemType> *p1 = head, *p2;
tail->SetLink(NULL); //先拆散循环再逐一销毁,前提是尾指针维护的正确,否则会导致下面重复释放
while (p1 != NULL)
{
p2 = p1;
p1 = p1->next;
delete p2;
}
}
int ListLength() const { return lenth; } //返回链表的长度
LinkNode<ElemType> *GetHead() const { return head; } //获取链表头结点
void SetHead(LinkNode<ElemType> *p) { head = p; } //设置链表头结点
void SetTail(LinkNode<ElemType> *p) { tail = p; } //设置表尾节点
void SetLenth(int l) { lenth = l; } //设置链表长度
LinkNode<ElemType> *GetTail() { return tail; } //获取链表尾结点
//遍历链表
bool ListTraverse() const
{
if (lenth == 0)
return 0;
LinkNode<ElemType> *p1 = head;
for (int i = 0;p1->next!=head; i++) //不以lenth为标准
{
p1 = p1->next;
if (i)
cout << "->";
cout << "(" << p1->getNumber()<<","<<p1->getCode()<<")";
}
return 1;
}
void add(int n,int e)
{ //表尾添加节点
LinkNode<ElemType> *p1 = new LinkNode<ElemType>(n,e);
tail->SetLink(p1);
tail = p1;
p1->SetLink(head);
lenth++;
}
};
template<class ElemType>
void Joseph(CirLinkList<ElemType> &A, int m){
int i=1,c=0;
LinkNode<int> *p1=A.GetHead()->next,*p2=A.GetHead();
while(1){
if(p1==A.GetHead()){ //跳过头结点
A.SetTail(p2); //维护尾指针,若遗忘会重复释放内存从而报错,见函数CirLinkList::ListDestroy()
if(p1->next==A.GetHead()) break; //空表退出
p2=p1;
p1=p1->next;
}
if(i==m){
if(c) cout<<"->";
c++;
cout<<p1->getNumber();
p2->SetLink(p1->next);
m=p1->getCode(); //重置报数密码
delete p1;
A.SetLenth(A.ListLength()-1);
p1=p2->next;
i=1; //重置报数
} else {
i++;
p2=p1; //忘了迭代了= =
p1=p1->next;
}
}
return ;
}
int main()
{
int n,tmp,m;
cin>>n;
CirLinkList<int> List;
for(int i=0;i<n;i++){
cin>>tmp;
List.add(i+1,tmp);
}
cin>>m;
List.ListTraverse();
cout<<endl;
cout<<endl;
Joseph(List,m);
cout<<endl;
return 0;
}
这里DeBug时发现一个小问题,当p2==head
,delete p2
后p2!=NULL
但是p2==head
,感觉怪神奇的= =
圆桌问题
要求:圆桌上围坐着2n个人。其中n个人是好人,另外n个人是坏人。如果从第一个人开始数数,数到第m个人,则立即处死该人;然后从被处死的人之后开始数数,再将数到的第m个人处死……依此方法不断处死围坐在圆桌上的人。试问预先应如何安排这些好人与坏人的座位,能使得在处死n个人之后,圆桌上围坐的剩余的n个人全是好人。
可以暴力模拟也可以跳过无用循环,直接使用循环链表的ADT即可,以下是主要函数:
void TraverseJoseph(CirLinkList<int> &A){ //判定好坏并输出,0为好人,1为坏人
int n=A.ListLength(),c=1;
LinkNode<int> *p1=A.GetHead()->next;
while(p1!=A.GetHead()){ //循环链表的尾结点判定别弄错!
if(p1->getData()) cout<<"B";
else cout<<"G";
if(c==50 && p1->next!=A.GetHead()){
cout<<endl;
c=0;
}
c++;
p1=p1->next; //别老是忘了迭代啊= =
}
}
void Joseph(CirLinkList<int> &A,int m){ //约瑟夫环模拟
int i=1,c=0,n=A.ListLength()/2;
LinkNode<int> *p1=A.GetHead()->next;
while(c<n){
// cout<<c<<endl; //调试循环情况
if(p1==A.GetHead()){ //跳过头结点,同时省略无意义的迭代,直接跨到最后一轮
int k1=m-i,k2=2*n-c;
if(k1%k2==0){ //恰好整数轮
if(k1/k2>=1){ //多于一轮
i+=(k1/k2-1)*m;
}
} else {
if(k1/k2>0){ //不只一轮
i+=(k1/k2)*k2; //注意这里不是乘以m,是乘以k2! (*)
}
}
}
if(p1->getData()){ //若已被击倒则跳过
p1=p1->next;
continue;
}
if(i==m){
i=1;
p1->SetData(1);
c++; //计数击倒的人
} else {
i++;
}
p1=p1->next;
}
}
(*)处写的时候犯了个错,乘了个m导致i大于m死循环
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具