可变数组
typedef 关键字用法:
用法1:为类型取一个新的名字。
下面的实例为单字节数字定义了一个术语 BYTE:
typedef unsigned char BYTE;
在这个类型定义之后,标识符 BYTE 可作为类型 unsigned char 的缩写,例如:
BYTE b1, b2;
用法2:为用户自定义的数据类型取一个新的名字。
应用:可变数组
C语言本身提供的数组,在定义时就需要指定数组的长度。然而在一些场景下并不会知道需要用数组取储存的数据有多少个,无法明确定义数组的长度。强行定义一个很大很大的数组又会浪费机器的内存空间
。这种情况下就需要一个长度可扩展的数组。
实现过程:
- 构造数组——使用
结构体
来定义可变数组,包括:①.数组头部地址int *array
②数组长度int szie
- 实现可变数组——实现可变数组的过程包括 创建数组及 下标查询、 数组长度查询、 扩展数组、 释放数组内存空间等功能。
#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为单位
可变数组的缺点
- 在每一次的扩展过程中都需要做一次原数组的拷贝,时间复杂度为O(n),当数组很长的时候很浪费时间。
- 每一次扩展都新申请一块大于原数组空间的新的内存区域,再将原数组内存空间释放出去。当数组足够大的时候,可能会出现内存中还有空间,数组却不能扩展的情况。
链表
链表是一种物理存储单元上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的–百度百科
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 = NULL;
int 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;
}