第3章 链表及链式栈、链式队列--1

时间:2024-09-13 14:29:08 分类:信息学

3 链表及链式栈、链式队列

3.1 指针(Pointer

1、什么是指针?

指针(Pointer):变量的地址,通过它能找到以它为地址的内存单元。

例子:理解指针的概念,区分什么是地址(指针),什么是地址指向的值!

理解如何通过指针修改变量的值!

#include <bits/stdc++.h>

using namespace std;

int main(){

int x = 10;

//定义指针,指向x的地址 int *p = &x;

//pint*类型的指针(整数的地址) cout<<p<<endl;

//*p不是地址,是p这个地址指向的值 cout<<*p<<endl;

*p = *p + 2;//修改指针指向的变量的值

cout<<p<<" "<<*p<<endl;

cout<<x<<endl;

//数组的本质是数组中下标为0的元素的地址 int arr[5] = {0};

cout<<&arr<<" "<<&arr[0]<<" "<<arr<<endl;

//采用scanf读入 int a,b; scanf("%d%d",&a,&b);

printf("a=%d\n",a); printf("b=%d\n",b); printf("%d+%d=%d\n",a,b,a+b);

return 0; }

问题:对比如下代码输出的结果,理解指针和普通变量的不同;

#include <bits/stdc++.h>

using namespace std;

int main(){

int x = 10; int y = x; y = y + 2; cout<<y<<endl<<x<<endl;

int *p = &x; *p = *p + 2; cout<<*p<<endl<<x<<endl;

int a = 10; int *p2 = &a;

cout<<p2<<" "<<a<<endl;

(*p2)++;//注意++的优先级高于*

cout<<p2<<" "<<a<<endl;

return 0; }

例子:*p++和(*p)++的区别

int x = 10; int *p = &x; cout<<p<<endl<<x<<endl;

*p++; cout<<p<<endl<<x<<endl;

注意:上述代码并不能通过指针修改 x 的值,*p++相当于 p++是在对指针的值自增(相

当于地址值自增)。

2、结构体指针示例

例子:理解结构体指针的各种定义方法。

#include <bits/stdc++.h>

using namespace std;

struct stu{

 char name[100];

 double score;

};

int main(){

//定义结构体变量 struct stu s;

strcpy(s.name,"ZhangSan");

s.score = 99.99;

//输出结构体

cout<<s.name<<" "<<s.score<<endl;

//定义指针,指向结构体struct stu *stu = &s;//struct可以省略 stu *stu1 = &s;

//在结构体指针中,不能用.来指向结构体成员变量(stu是地址)

cout<<stu1->name<<" "<<stu1->score<<endl;

//(*stu)是结构体

cout<<(*stu1).name<<" "<<(*stu1).score<<endl;

//new结构体对象,直接赋值给一个指针 stu *stu2 = new stu; strcpy(stu2->name,"ZhangSan");

stu2->score = 99.8;

cout<<stu2->name<<" "<<stu2->score<<endl; delete stu2;//释放内存

//malloc结构体对象,直接赋值给一个指针 stu *stu3 = NULL;

stu3 = (stu*)malloc(sizeof(stu));

strcpy(stu3->name,"WangWu");

stu3->score = 98;

cout<<stu3->name<<" "<<stu3->score<<endl; free(stu3);//释放内存

return 0; }

注意:理解 malloc free 函数的作用!

(1)malloc:动态内存分配,用于申请一块连续的指定大小的内存块区域并返回分配

的内存区域地址。

(2)free函数能释放某个动态分配的地址,表明不再使用这块动态分配的内存了,实

现把之前动态申请的内存返还给系统。

(3)如果结构体中有 string 类型的成员,那么不能使用 malloc 动态分配内存(可以

使用 new),这是因为 malloc/free new/delete 的重要区别之一是:

new 会先调用 operator new 函数,申请足够的内存(通常底层使用 malloc 实现)。然

后调用类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete 先调用析构

函数,然后调用 operator delete 函数释放内存(通常底层使用 free 实现)。

malloc/free是库函数,只能动态的申请和释放内存,无法强制要求其做自定义类型对

象构造和析构工作。

所以 string 的构造函数并没有被调用,name 没有被初始化,改为 new 能正常运行。

例子:通过函数输出结构体的 2 种方法示例

#include <bits/stdc++.h>

using namespace std;

struct stu {

 string name;

 double score;

};

void show(struct stu s) {

 cout<<s.name<<" "<<s.score<<endl;

}

void show2(struct stu *s) {

 cout<<s->name<<" "<<s->score<<endl;

}

int main() {

struct stu s;

s.name = "ZhangSan";

s.score = 99;

show(s);

stu *t = &s;

show2(t); return 0;

}

3.2 链表

1、链表介绍

(1)线性表的优点

A、无需为表示结点间的逻辑关系而增加额外的存储空间;

B、可方便地随机存取表中的任一元素。

(2)线性表的缺点

A、插入或删除平均需要移动一半的结点;

B、顺序表要求占用连续的存储空间;

问题:存储一个班同学的成绩(人数不确定),如何存储?

解决方法 1:定义尽可能长的数组来存储!

99 100 98 90 88 97 100 92 98

解决方法 2:利用链表来存储!

注意:链表结构并不一定占用连续的内存空间,其占用的内存空间可以不连续!

(3)链表的基本概念

A.结点(Node)组成

数据域 指针域

B.数据域:存储数据元素本身

C.指针域:存储邻接元素的存储地址(位置)

D.链接方式:

单链表:每个结点只有一个指针域。

双向链表:每个结点有两个指针域。

循环链表:是一个首尾相接的链表。

E.头指针 :指向链表头结点的指针。

2、链表的基本操作

(1)要实现的基本操作:

add(int x):在链表末尾追加元素(注意头结点)

insert(int n,int x):在链表中插入一个元素(注意头结点)

deldata(int data):删除链表中第一个出现的 data 的值(注意:如果 data 是头节点)

delpos(int n):删除链表的第 n 个节点(注意如果 n==1,也就是删除头结点的情况)

display():显示链表

(2)实现代码:

#include <bits/stdc++.h>

using namespace std;

//结点定义

struct Node{

 int data;

 struct Node *next;

};

//头结点

Node *head = NULL;

//在链表末尾追加

void add(int x){

 //如果有 head的值

 if(head != NULL){

 //要追加的节点

Node *a = new Node;

a->data = x;

a->next = NULL;

Node *p = head;

//移动到最后一个节点

while(p->next != NULL){

 p = p->next;

}

 p->next = a;

}else{

 head = new Node;

 head->data = x;

 head->next = NULL;

}

}

//在中间追加元素(在第 n个元素的位置)

void insert(int n,int x){

 //新节点

Node *d = new Node;

d->data = x;

d->next = NULL;

//如果是头结点

if(n == 1){

d->next = head;

head = d;

}else{

int i;

Node *p = head;

for(i = 1;i <= n - 2;i++){

 p = p->next;

if(p == NULL){

 break;

} }

if(p == NULL){

 cout<<"n有误"<<endl;

}else{

d->next = p->next;

 p->next = d;

}

} }

//删除链表中第一个出现的 data

void deldata(int data){

 Node *p = head,*pre = NULL;

 while(p != NULL){

if(data == p->data){

 if(p == head){

head = p->next; }else{

 pre->next = p->next;

}

delete p;

break;

}

pre = p;

p = p->next; }

}