C语言复习——可变数组到链表

本文探讨了C语言中可变数组的概念及其实现,包括利用typedef创建类型别名,以及如何构建和扩展可变数组。同时,文章介绍了链表的基本原理,如单链表的结构,并讲解了链表的节点定义、函数操作、搜索、删除和清除等操作。虽然可变数组在扩展时存在效率问题,但链表提供了灵活的数据存储解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可变数组

typedef 关键字用法:
用法1:为类型取一个新的名字。
下面的实例为单字节数字定义了一个术语 BYTE:

typedef unsigned char BYTE;

在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:

BYTE  b1, b2;

用法2:为用户自定义的数据类型取一个新的名字。
应用:可变数组
C语言本身提供的数组,在定义时就需要指定数组的长度。然而在一些场景下并不会知道需要用数组取储存的数据有多少个,无法明确定义数组的长度。强行定义一个很大很大的数组又会浪费机器的内存空间。这种情况下就需要一个长度可扩展的数组。

实现过程:

  1. 构造数组——使用结构体来定义可变数组,包括:①.数组头部地址 int *array ②数组长度 int szie
  2. 实现可变数组——实现可变数组的过程包括 创建数组下标查询数组长度查询扩展数组释放数组内存空间等功能。
#include <stdio.h>
#include <stdlib.h>

const int BLOCK_SIZE = 20;
typedef struct{//构造数组
	int *array;
	int size;
} Array;//为什么不在这里定义指针?—— 如果在这里定义指针的话,那么在函数内定义的Array a一定是从某个地方被制造出来的,比如说动态分配的内存,而不是本地变量。

Array array_create(int init_size)//创建数组
{
	Array a;
	a.size = init_size;
	a.array = (int*)malloc(sizeof(int)*a.size);
	return a;
}

void array_free(Array *a)//释放数组内存空间
{
	free(a->array);
	a->array = NULL;
	a->size = 0;
}

//封装
int array_szie(const Array *a)//数组长度查询 
{
	return a->size;
}

void array_inflate(Array *a, int more_size)//扩展数组 
{
	int *p = (int*)malloc(sizeof(int)*(a->size + more_size));//重新malloc一块新的空间
	int i;
	for(i=0; i<a->size; ++i)
		p[i] = a->array[i];//拷贝a到p
	free(a->array);
	a->array = p;
	a->size += more_size;
}

//为什么要返回指针呢? —— *array_at(&a, 0) = 10;不仅可以实现查询功能,还能实现修改功能
int* array_at(Array *a, int index)//数组下标查询
{
	if(index >= a->size)//数组越界
		array_inflate(a, (index/BLOCK_SIZE+1)*BLOCK_SIZE-a->size);
	return &(a->array[index]);
}


int main(int argc, char const *argv[])
{
	Array a = array_create(20);
	printf("%d\n", array_szie(&a));
	*array_at(&a, 0) = 10;
	printf("%d\n", *array_at(&a, 0));
	int cnt = 0;
	int number;
	while(1){
		scanf("%d", &number);
		if(number == -1)
			break;
		*array_at(&a, ++cnt) = number;
		printf("%d  %d\n",cnt, number);//输出下标及对应的值
	}
	array_free(&a);
	return 0;
}

演示扩展数组中的Block思想:
每次增长按照Block为单位
在这里插入图片描述
可变数组的缺点

  1. 在每一次的扩展过程中都需要做一次原数组的拷贝,时间复杂度为O(n),当数组很长的时候很浪费时间。
  2. 每一次扩展都新申请一块大于原数组空间的新的内存区域,再将原数组内存空间释放出去。当数组足够大的时候,可能会出现内存中还有空间,数组却不能扩展的情况。

链表

链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的–百度百科
在这里插入图片描述

n个元素的线性表通过每个结点的指针域拉成一个“链子”,称之为链表,在下图所示的结构中,因为每个结点中只有一个指向后继的指针,所以称其为单链表。
在这里插入图片描述
定义结点:
int value 代表数据域,可以自定义数据类型
struct _node 代表指针域,指向直接后继元素
Node 是结点名,每个结点都是一个 _node 结构体
提示:由于指针域中的指针要指向的也是一个结点,因此要声明为 _node 类型。
在这里插入图片描述
错误定义:因为结点还没有定义完成,不能用已经定义完成的结构名
在这里插入图片描述

node.h
#ifndef _NODE_H_
#define _NODE_H_

typedef struct  _node{
	int value;
	struct _node *next;
} Node;
#endif
linked-list.c
#include "node.h"
#include <stdio.h>

//typedef struct _node{
	int value;
	struct _node *next;
} Node;

int main(int argc,char const *argv[])
{
	Node *head = NULLint number; 
	do{
		scanf("%d",&number);
		if(number != -1)
		{	
			//add to linked—list
			Node *p = (Node*malloc(sizeof(Node));
			p->value = number;
			p->next = NULL;
			//find the last
			Node *last = head;
			if(last){//先让last指向head,然后遍历直到读到的last=NULL
				while(last->next){
					last=last->next;
				}
				//attach
				last->next=p;
			}else{//head=NUll的时候,直接让head指向p
				head=p;
			}
		}
	}while(number != -1)
		
	return 0;
}

链表的函数

引用传递和值传递:这时要函数内部返回 Node *p 使得对函数外部的主函数有影响,就要使用二级指针,所以我们改用容器来储存指针参数。
二级指针作为函数参数的作用:在函数外部定义一个指针p,在函数内给指针赋值,函数结束后对指针p生效,那么我们就需要二级指针。

void add(List* pList,int number)
{
	//add to linked-list
	Node *p =(Node*malloc(sizeof(Node));
	p->value = number;
	p->next = NULL;
	//find the last
	Node *last = pList->head;
	if(last){
		while(last->next){
			last = last->next;
		}
		//attach
		last->next = p;
	}else{
		pList->head = p;
	}
}

链表的搜索

void print(List *pList){
	Node *p;
	for(p=pList->head;p;p=p->next){
		printf("%d\t",p->value);
	}
	printf("\n");
}

链表的删除和清除

#include<stdio.h>
#include<stdlib.h>
typedef struct node{
	int value;
	struct node *next;
}Node;
typedef struct _list{
	Node* head;
}List;
void add(List* pList,int number){
	//add to linked-list
	Node *p=(Node*)malloc(sizeof(Node));
	p->value=number;
	p->next=NULL;
	Node *last=pList->head;
	//find the last
	if(last){
		while(last->next){
			last=last->next;
		}
		last->next=p;
	}else{
		pList->head=p;
	}
			
}
void print(List *pList){
	Node *p;
	for(p=pList->head;p;p=p->next){
		printf("%d\t",p->value);
	}
	printf("\n");
}
int main(){
	//Node *head=NULL;
	List list;
	list.head=NULL; 
	int number;
	//添加 
	do{
		scanf("%d",&number);
		if(number!=-1){
			add(&list,number);
			
		}
	}while(number!=-1);
	//打印 
	print(&list); 
	
	//找到一个值并删除 
	int isFound=0;
	Node *p;
	for(p=list.head;p;p=p->next){
		if(p->value==number){
			printf("找到了\n");
			isFound=1;
			break;
		}
	}
	if(!isFound){
		printf("没找到\n");
	}
	Node* q; 
	for(q=NUll,p=list.head;p;q=p,p=p->next){
		if(p->value==number){
			//找边界: Any pointer at the left of -> must be checked 
			if(q){
				q-next=p->next;			
			}else{
				list.head=p->next;
			}
			
			free(p); 
			break;
		}
	}
	
	//整个链表的清除 
	for(p=list.head;p;p=q){
		q=p->next;
		free(p);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值