数据结构学习日记1---链表(附结构体)

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;
	}
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值