第四周 可变数组和链表——慕课笔记

第四周 可变数组和链表——MOOC慕课笔记


1.可变数组

  • 实现思路(利用结构变量代表数组变量)
    • 数组大小要可变的
    • 能获知当前数组大小

    • 访问数组的元素

  • 自定义结构类型

typedef  struct{
    
    
    int *array;
    int size;
} Array;//不定义结构指针
  • 可变数组的创建
    • 不返回结构指针:调用函数内部的变量都是临时创建的,函数返回时将会被释放,如果返回指针,指向的本地变量将会失效
    • 不建议用传结构的指针进去修改其值,有可能结构的指针指向的内存空间是动态分配的,这时候需要free;又有可能a–>NULL,这时候会出错
Array array_create(int init_size){
    
    
    Array a;
    a.array = (int*)malloc(sizeof(int)*init_size);//动态分配的内存空间函数调用返回时不会被释放
    a.size = init_size;
    return a;//不返回结构指针return &a;
}
  • 可变数组大小的访问(封装——不把内部实现细节暴露)
int array_size(const Array *a){
    
    
    return a->size;
}
  • 可变数组元素的访问
int *array_at(Array *a,int index){
    
    
    return &(a->array[index]);//即可访问该值又可修改该值
}
  • 可变数组的长大(重要)

    • 方法一(简单易懂但不方便)
    int array_inflate(Array *a,int more_size){
          
          
        int *p = (int*)malloc(sizeof(int)*(a->size + more_size));
        for (int i = 0;i<a->size;++i){
          
          
            p[i] = a->array[i];//复制原数组的元素
        }
        free(a->array);
        a->array = p;
        a->size += more_size;
    }
    
    • 方法二(block的概念——每次增加一个块)
    const int Block_Size = 20//每个块的大小存放20个元素
        
    int *array_at(Array *a,int index){
          
          
        if ( index >= a->size)//每次增加以一个块为基准
            //(index/Block_Size+1)*Block_Size = 增加元素后数组的大小
            array_inflate(a, (index/Block_Size+1)*Block_Size-(a->size));
        return &(a->array[index]);//即可访问该值又可修改该值
    }
    
    int array_inflate(Array *a,int more_size){
          
          
        int *p = (int*)malloc(sizeof(int)*(a->size + more_size));
        for (int i = 0;i<a->size;++i){
          
          
            p[i] = a->array[i];//复制原数组的元素
        }
        free(a->array);
        a->array = p;
        a->size += more_size;
    }
    

2.单向链表

  • 可变数组的设计缺陷
    • 每一次扩展数据都需要拷贝数组
    • 每一次扩展数组都需要申请新的内存(有可能明明内存足够但是却申请不了)
      • 需要allocated+more但是allocated需要在新内存分配后才能释放
  • 链表——linked blocks(将分配的新块链接起来)

    • 每个结点存放数据以及指向下一个结构的指针
    • 每个结构称作结点node
    • 末尾结构内的指针指向NULL
  • 链表元素的新增

  • 方法一

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

//读入数据实现的核心代码
Node * head =NULL;
//增加结点
Node *p =(Node*)malloc(sizeof(Node));
p->value = number;
p->next = NULL;
//寻找链表尾部
Node *last = head;
if (last){
    
     //head 不为NULL
    while (last->next)//寻找指向最后结点的指针
    	last = last->next;
    last->next = p;//找到最后一个节点,将新结点拼接在其后面
}else head = p;
  • 方法二(自定义List结构——只有head无tail)
    • 好处:自定义数据结构来代表整个链表,给将来升级改造整个链表带来无限可能,否则head只是光秃秃的一个head,没有具体意义的head
    • 注意:因为指针head的内容有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值(pp指向p,p指向具体的内容——需要传进去的是指针p的地址才能够修改其内容
typedef struct _node{
    
    
    int value;
    struct _node *next;
}Node;

typedef struct _list{
    
    
    Node * head;
//    Node * tail;
}List;//自定义一个List结构来代表链表

//读入数据实现的核心代码
List list;//初始化list
list.head = list.tail = NULL;
//增加结点的函数
void add(List * plist,int number){
    
    
    //因为head的数据有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值
    //增加结点
	Node *p =(Node*)malloc(sizeof(Node));
	p->value = number;
	p->next = NULL;
    //find the last
    Node *last = plist->head;
	if (last){
    
     //head 不为NULL
        while (last->next)//寻找指向最后结点的指针
    		last = last->next;
        last->next = p;//找到最后一个节点,将新结点拼接在其后面
	}
    else
        plist->head = p;
}
  • 方法三(自定义List结构——有head有tail)
typedef struct _node{
    
    
    int value;
    struct _node *next;
}Node;

typedef struct _list{
    
    
    Node * head;
    Node * tail;
}List;//自定义一个List结构来代表链表

//读入数据实现的核心代码
List list;//初始化list
list.head = list.tail = NULL;
//增加结点的函数
void add(List * plist,int number){
    
    
    //因为head的数据有时候需要被修改,所以传进去的值需要是指向指针的指针,这样才可以修改第一重指针的值
    //增加结点
	Node *p =(Node*)malloc(sizeof(Node));
	p->value = number;
	p->next = NULL;
    //folow the last
    if ( plist->head == NULL)//链表没有元素
        plist->head = plist->tail = p;
   	else {
    
    //链表已有至少一个元素,head不需要动,只需将元素接着每个尾巴指向的next后面,并且使该尾巴指向后一个插入结点
   	    plist->tail->next = p;
   		plist->tail = p;
    }
}	
  • 链表的遍历
void printf(List *plist){
    
    
    Node *p;
    for (p=plist->head; p; p= p->next)
		printf("%d\t",p->value);
    printf("\n");
}
  • 链表元素的查找
void find(List *plist,int number){
    
    
    Node *p;
    bool isFound = false;
    for (p=plist->head; p; p= p->next)
		if (p->value == number){
    
    
            printf("找到了\n");
            isFond = true;
            break;
        }
    if (!isFound )
        printf("没找到\n");
}
  • 链表元素的删除

    ‘->’符号右边的指针必须check是否为NULL,否则会出错(边界条件需注意

void delete(List *plist,int number){
    
    
    Node *p,*q;
    for (q=NULL,p=plist->head; p; q=p,p=p->next){
    
    
		if (p->value = number){
    
    
            if ( q )//第一个元素即为所删除元素,此时q=NULL
            	plist->head = p->next;    
            else q->next = p->next;                    }
    	free(p);
        break;
    }
}
  • 链表的清除
void free_List(List *plist){
    
    
    Node *p,*q;
    for (p = plist->head; p ; q = p;){
    
    
        q = p->next;
        free(p);  
    }
}

猜你喜欢

转载自blog.csdn.net/jimmy33777/article/details/105301418