8月26日学习了数据结构的前几章,学习到链表时对于C语言的结构体与链表时,决定先回顾一下c++中的结构体与链表。
c++中可以用两种方式说明结构体:
struct Student{}stu1,*stu2; //stu2是Student类型的指针变量
//或者
struct Student{};Student stu1,*stu2;
访问结构时可以用结构变量名.数据成员
的格式。如果是指针变量用*(指针变量).数据成员
或者(指针变量)->数据成员
的格式。当结构类型变量作为函数参数,也可选择作为传值参数、指针参数或引用参数。和c不同,c++允许在结构体内部加入成员函数。
除去普通的结构变量、结构指针,c++中还提供了结构数组。结构数组可以用在有许多同一结构的对象时,方便批量储存管理信息。下面看一个职工登记表,按薪资排序:
#include <iostream>
using namespace std;
struct person
{
char name[10];
unsigned int id;
double salary;
};
void Input(person[], const int);
void Sort(person* [], const int);
void Output(person* [], const int);
int main()
{
person allone[100]; //说明结构数组
person* index[100]; //说明索引数组
int total;
for (int i = 0; i < 100; i++) //索引数组元素值初始化为结构数组元素地址
index[i] = allone + i;
cout << "输入职工人数:" ;
cin >> total;
cout << "输入职工信息:" << endl;
Input(allone, total);
cout << "以工资为关键字排序:" << endl;
Sort(index, total);
Output(index, total);
}
void Input(person all[], const int n)
{
int i;
for (i = 0; i < n; i++)
{
cout << i << ":姓名:";
cin >> all[i].name;
cout << "编号:";
cin >> all[i].id;
cout << "工资:";
cin >> all[i].salary;
}
}
void Sort(person* pi[], const int n)
{
int i, j;
person* temp;
for (i = 1; i < n; i++)
{
for(j=0;j<n-i-1;j++)
if (pi[j]->salary > pi[j + 1]->salary)
{
temp = pi[j];
pi[j] = pi[j + 1];
pi[j + 1] = temp;
}
}
}
void Output(person* pi[], const int n)
{
for (int i = 0; i < n; i++)
cout << pi[i]->name << '\t' << pi[i]->id << '\t' << pi[i]->salary << endl;
}
在上面的程序中,先定义了person结构体,并且用该结构体说明一个数组和一个指针数组。让指针数组的每个指针都指向对应的数组元素。加这个指针数组的原因是,在sort函数内排序时,如果直接对结构体数组allone进行排序,会造成较大的数据开销。而对指针索引进行顺序调整更加简便。并且在sort函数中,结构体指针数组作为指针参数传入函数,实现对实际参数的操纵。最终输出结果:
输入职工人数:3 输入职工信息: 0:姓名:zzy 编号:001 工资:3000 1:姓名:zhb 编号:002 工资:6000 2:姓名:zht 编号:003 工资:5000 以工资为关键字排序: zzy 1 3000 zht 3 5000 zhb 2 6000
c++链表:创建链表
struct Node
{
char name[20];
double salary;
Node *next; //next是自身结构类型的指针,但上面的数据成员不能是自身结构的变量
};
创建单向链表:
struct Node
{
int data;
Node *next; //next是自身结构类型的指针,但上面的数据成员不能是自身结构的变量
};
void CreatList(Node* & head) //引用表头指针
{
Node *s,*p;
s=new Node;
cin>>s->data;
while(s->data!=0)
{
if(head==NULL)
head=s; //如果原来没有表头,那么s就是表头
else
p=s;
s=new Node;
cin>>s->data;
p->next=s; //如果已有表头,那么建立s的后继结点p,给s开辟新空间并赋值,之后让p的结点指针指向s
}
}
插入结点:分三种情况:
表头插入:生成新结点,赋值,新结点的下一跳指针指向原表头结点,并修改表头指针。 *p后插入与表头插入类似。 *p前插入:需要先找到*p之前的结点*q。也可避免重新搜索其前驱:
s->next=p->next; //后插
p->next=s;
temp=p->data;
p->data=p->next->data;
p->next->data=temp; //交换数据域
删除结点:也分三种情况,不再赘述。
经典问题“猴子选大王”:让 N 只候选猴子围成一圈(最多 100 只猴子),从某位置起顺序编号为 1 ~ N 号。从第 1 号开始报数,每轮从 1 报到 3 ,凡报到 3 的猴子即退出圈子,接着又从紧邻的下一只猴子开始同样的报数。如此不断循环,最后剩下的一只猴子就选为猴王。
下面记录一个“猴子选大王”的链表解法:
#include<iostream>
#include<iomanip>
using namespace std;
struct Jonse
{
int code;
Jonse* next;
};
Jonse* Create(int n)
{
Jonse *h, *p;
h = new Jonse;
p = h;
for (int i=1; i <= n; i++)
{
p->code = i; //每个结点顺序
if (i < n)
{
p->next = new Jonse;
p = p->next;
}
}
p->next = h; //形成闭环
return h;
}
void ShowList(Jonse* h)
{
Jonse* p;
p = h;
cout << "the list is:";
do
{
cout << p->code << '\t';
p = p->next;
} while (p != h);
cout << endl;
}
void Out(Jonse* h, int i, int d)
{
Jonse* p, * q;
int k;
p = h; //让p指向初始结点
for (q = h; q->next != h; q = q->next); //让q指向最后一个结点
for (k = 1; k < i; k++) //从第i个开始计,把指针移到该位置
{
q = p;
p = p->next;
}
while (p != p->next) //记到d进行处理,处理链环直至只剩一个结点
{
for (k = 1; k < d; k++)
{
q = p;
p = p->next;
}
cout << p->code << '\t'; //输出报到d的结点
q->next = p->next; //删除该结点
delete p; p = NULL;
p = q->next;
}
cout << p->code << endl; //处理最后一个结点
delete p;p = NULL;
}
int main()
{
Jonse *head;
int total, begin, num;
cout << "put total:" << endl;
cin >> total;
head = Create(total);
ShowList(head);
cout << "put strat number:" << endl;
cin >> begin;
cout << "put interval of counting:" << endl;
cin >> num;
Out(head, begin, num);
}
结果测试:
put total: 3 the list is:1 2 3 put strat number: 1 put interval of counting: 2 2 1 3
当然,还提供了一种数组解法:
#include <iostream>
using namespace std;
int out(int,int,int);
int main()
{
int total,begin,num, b;
cout << "input total number:" << endl;
cin >> total;
cout << "input start number:" << endl;
cin >> begin;
cout << "input interval of counting:" << endl;
cin >> num;
b = out(total,begin,num);
cout << "最终留下第" << b + 1 <<"个" << endl;
return 0;
}
int out(int total,int begin,int num)
{
int *a=new int[total];
int i, j = 0;
int t = begin-2; //-1是因为第一只猴子对应a[0];另一个-1因为从第一只已经开始计数了,抵消下面的计数+1
for (i = 0; i < total; i++)
a[i] = 1;
for (i = 0; i < total; i++) //total-1次循环最终留下一个大王
{
j = 1; //淘汰计数
while (j <= num) //如果没到淘汰计数
{
t = (t + 1) % total; //指向下一只猴子
if (a[t] == 1)j++; //淘汰计数+1
}
a[t] = 0; //淘汰该猴子
}
return t;
}
测试结果:
input total number: 3 input start number: 1 input interval of counting: 2 最终留下第3个
c语言链表,参考自《数据结构与算法c语言版》
先看一下typedef在c中的用法
typedef struct Student
{
int a;
}Stu; //于是声明变量时可以Stu stu1,如果没有typedef就必须用struct Student stu1来声明
//在c++中如果加了typedef,那么Stu将是一个结构体类型,声明变量还要Stu stu1,但不加typedef则Stu本身就是变量
//如果有
typedef struct {}a,b,c;那么a,b,c都是结构体别名,a stu1和b stu1 是一样的。
再来看链表的类型声明
struct Node; //声明Node结构体
typedef struct Node* PtrToNode; //typedef定义新类型,名字叫做PtrToNode,它的类型是指向Node结构体的指针
typedef PtrToNode List; //相当于typedef struct Node* List,和后面那句一起,给 PtrToNode起了两个别名
typedef PtrToNode Position;
List MakeEmpty(List L); //变量L的类型相当于typedef struct Node* List;List L;生成的,指向Node结构体的指针变量
int InEmpty(List L);
int IsLast(Position P, List L);
Position Find(ElementType X, List L);
void Delete(ElementType X, List L);
Position FindPrevious(ElementType X, List L);
void insert(ElementType X, List L, Position P);
struct Node
{
ElementType Element;
Position Next;
};
功能函数具体编写:
int InEmpty(List L); {return L->Next == NULL;}
int IsLast(Position P, List L); {return P->Next == NULL;}
Position Find(ElementType X, List L)
{
Position P;
P=L->Next;
while(P!= NULL && P->Element != X)
P=P->Next;
return P;
}
void Delete(ElementType X, List L);
{
Position P,Tmpcell;
P=FindPrevious(X,L); //找到P的前驱元素
if(!LsLast(P,L))
{
TempCell=P->Next;
P->Next=TempCell->Next;
free(Tmpcell);
}
}
Position FindPrevious(ElementType X, List L);
{
Position P;
P=L; //让结构指针P先指向表头
while(P->Next!= NULL && P->Next->Element!= X)
{
P=P->Next;
}
return P;
}
void insert(ElementType X, List L, Position P);
{
Position TempCell;
TempCell=malloc(sizeof(struct Node)); //定义一个Node结构体指针,指针名为TempCell,指向一个Node结构体,并为这个结构体分配了Node大小的内存空间
if(TempCell == Null)
FatalError("out of space!");
TempCell->Element = X;
TempCell->Next = P->Next;
P->Next = TempCell; //P后插
}
如何而知是否要用malloc获取一个新单元?---声明指向一个结构的指针并不会创建该结构,而只给出足够的空间容纳结构可能会使用的地址。创建尚未被声明过的记录的唯一方法是用malloc库函数。malloc是系统创建一个新的结构并返回指向该结构的指针。但如果仅仅想用一个指针变量沿着一个表进行,那么没必要创建新的结构,此时不宜使用malloc。
当空间不再需要,可以用free命令收回。free(P)时,P指向的地址没变,但该地址处数据被清空。
在用完一个链表,为了释放储存空间我们去删除它时,一种错误的思路是
void DeleteList(List L)
{
Position P;
P=L->Next;
L->Next=NULL;
while(P!=NULL)
{
free(P);
P=P->Next;
}
}
因为一旦free(P)释放了这个单元,就不再能引用它, P=P->Next也就无法实现.正确方法应如下:
void DeleteList(List L)
{
Position P,Tmp;
P=L->Next;
L->Next=NULL;
while(P!=NULL)
{
Tmp=P->Next;
free(P);
p=Tmp;
}
}