手撕链表各操作(插入、删除、排序)、指针操作

链表

#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <stdbool.h>

typedef struct Node{
    
       //单链表的节点类型
    int data;
    struct LNode * next;
}LNode, * PNode;  //LinkList相当于 struct Node * 链表节点的指针形式

//创建一个链表
PNode create_list(){
    
       
    int len;
    int val;  //临时存放用户输入的节点值

    printf("输入链表长度len:\n");
    scanf("%d", &len);
    //(struct Node *)/(PNode)将分配的空间强制转换为适合struct Node *的chunk,sizeof中填入LNode计算一个链表节点所占的字节数
    //不存放数据的头节点
    LNode * pHead = (LNode *)malloc(sizeof(LNode));
    if(NULL == pHead){
    
    
        printf("分配失败,程序终止!\n");
        exit(-1);
    }
    PNode pre = pHead;
    pre->next = NULL;
    for(int i = 0; i < len ; i++){
    
    
        printf("请输入该节点的值val:\n");
        scanf("%d", &val);
        LNode * pnode = (LNode *)malloc(sizeof(LNode));  
        //将pnode加入链表并赋值,在链表尾部设置为NULL
        pre->next = pnode;
        pnode->data = val;
        pnode->next = NULL;
        pre = pnode;
    }
    return pHead;
}

//遍历链表并输出每一个元素
void traverse_list(PNode pHead){
    
    
    printf("该链表内容如下:\n");
    PNode pcur = pHead->next;   //pHead为空
    while (pcur != NULL)
    {
    
    
        printf("%d ", pcur->data);
        pcur = pcur->next;
    }
    printf("\n");
    return;
}

//返回链表是否为空
bool is_Empty(PNode pHead){
    
    
    if(pHead->next == NULL){
    
    
        return true;
    }else{
    
    
        return false;
    }
}

//返回链表长度
int length_list(PNode pHead){
    
    
    PNode pcur = pHead->next;
    int len = 0;
    while (pcur != NULL)
    {
    
    
        len++;
        pcur = pcur->next;
    }
    return len;
}

//在第pos个位置上(pos从1开始)插入一个元素data, 返回是否操作成功  len=5则pos可以为6不能为7
bool insert_list(PNode pHead, int pos, int val){
    
    
    PNode pcur = pHead;  //初始节点为第一个节点的前驱节点(头节点)
    int len = length_list(pHead);  //获取链表长度
    //首先判断输入是否合理
    if(pos < 1 && pos > len + 1){
    
    
        return false;
    }
    //找到前驱节点pcur
    for(int i = 1; i < pos; i++){
    
       
        pcur = pcur->next;  
    }
    //创建一个新的节点并插入
    PNode pNew = (PNode)malloc(sizeof(LNode)); 
    if(NULL == pHead){
    
    
        printf("分配失败,程序终止!\n");
        exit(-1);
    }
    pNew->data = val;
    pNew->next = pcur->next;
    pcur->next = pNew;
    return true;

}

//删除第pos个位置上的元素,并将删除的元素返回
bool delete_list(PNode pHead, int pos, int * p){
    
    
    PNode pcur = pHead;  //初始节点为第一个节点的前驱节点(头节点)
    int len = length_list(pHead);  //获取链表长度
    //首先判断输入是否合理
    if(pos < 1 && pos > len){
    
    
        return false;
    }
    //找到前驱节点pcur
    for(int i = 1; i < pos; i++){
    
       
        pcur = pcur->next;  
    }
    //删去q
    PNode q = pcur->next;   //q为需要删去的节点, 在这里不能直接写pcur->next->next
    pcur->next = q->next;
    *p = q->data;
    free(q);
    return true;
}

//链表的排序(选择排序) 升序
void sorted_list(PNode pHead){
    
    
    PNode p, q;  //p为左侧待比较指针,q为右侧遍历指针
    int t;

    for(p = pHead->next ; p->next != NULL ; p = p->next){
    
      //左指针移动, 循环条件:下一个元素不为空
        for(q = p->next ; q != NULL ; q = q->next){
    
       //右指针移动,循环条件:当前指针不为空
            if(p->data > q->data){
    
       //出现更小的值就进行交换
                t = p->data;
                p->data = q->data;
                q->data = t;
            }
        }
    }
}

int main(){
    
    
    PNode pHead = NULL;  //必须提前声明Phead并设为NULL
    pHead = create_list();  //创建链表
    int val;  //保存删去元素的值
    traverse_list(pHead);  //遍历输出链表
    printf("链表长度为:%d\n", length_list(pHead));
    // sorted_list(pHead);
    traverse_list(pHead);
    printf("插入一个元素\n");
    if(insert_list(pHead, 3, 100) == false){
    
    
        printf("输入不合法!\n");
    }
    traverse_list(pHead);
    printf("删去一个元素\n");
    if(delete_list(pHead, 2, &val) == false){
    
    
        printf("输入不合法!\n");
    }
    printf("删去的值为:%d\n", val);
    traverse_list(pHead);
    return 0;
}

指针(地址)

指针基础

cpu和内存的交互:地址线、控制线、数据线(以4G内存为例)

  • 指针==地址

  • 指针变量是存放内存单元地址的变量

  • 指针的本质是一个操作受限的非负整数

    int * p; //p是一个变量名,int * 表示该变量只能存储int类型变量的地址
    int i = 10;  //假设i的地址为2000H,存储的值为10
    int j;

    // p = 10;不可以存放值
    p = &i;  //p可以存放地址,即为i的地址2000H,可以称作p指向了i,则*p就代表了i
    j = *p; //等价于j = i
    printf("i = %d, j = %d, p = %d\n", i, j, *p);
int *p = &i; //等价于int *p; p = &i;

通过被调函数修改主函数中的普通变量的值:

void f(int *p){
    
      //定义一个指针变量(形参)
    *p = 100; //将p指向的地址内的数值改为100
}

void function2(){
    
    
    int i = 9;

    f(&i);
    printf("i = %d\n", i);
}

指针和一维数组

  1. 一维数组名是一个指针常量
  2. a[i] 等价于 *(a+i)
  3. p±i的值是p±i*(p所指向的变量所占的字节数)
void function3(){
    
    
    int a[5] = {
    
    1, 2, 3, 4, 5};
    // 3[a] = 0;  //等价于*(3+a)即a[3]
    // printf("%d", a[3]);
    a[3] == *(3 + a);
    //(a+1)代表的是a+1(数组第二个元素)的地址 数组每元素长度4个字节所以输出结果是以4为间隔
    //实质上为 a+sizeof(int)
    printf("%p  %p  %p  %p  \n", a+1, a+2, a+3, a+4);
    printf("%d\n", *a+10);  //取到*a的值后再加10

}

指针与地址

32位指针4个字节,64位指针8个字节,且用第一个字节的地址表示整个变量的地址:

void function5(){
    
    
    double * p;
    double x = 66.6;
    //32位指针4个字节,64位指针8个字节
    p = &x; //x占8个字节,1个字节8位,但是p只保存首地址(第一个字节的地址)

    double arr[3] = {
    
    1.1, 2.2, 3.3};
    
    printf("%p\n", &arr[0]);
    printf("%p\n", &arr[1]);

}

指针传入函数

void Show_Array(int * p, int len){
    
    
    
    for(int i = 0;i<len;i++){
    
    
        printf("%d\n", p[i]);
    }
}

void function4(){
    
    
    int a[5] = {
    
    1, 2, 3, 4, 5};
    Show_Array(a, 5);  //a等价于&a[0]
}

通过函数修改实参的值:

void f2(int ** q){
    
      //p本身为指针,指针的地址传入,所以类型为int **
    *q = (int *)0xFFFFFFFF;
}


void function6(){
    
    
    int i = 9;
    int * p = &i; //int * p; p = &i;

    printf("%p\n", p);
    f2(&p);  //希望改写p,所以需要发送p的地址
    printf("%p\n", p);
}

结构体

  1. 结构体变量不能加减乘除,可以相互赋值
  2. 普通结构体变量和结构体指针
struct Student{
    
    
    int sid;
    char name[200];
    int age;
};    //分号不能省略

void function1(){
    
    
    struct Student st = {
    
    1000, "zhangsan", 20};
    st.sid = 100;
    strcpy(st.name, "lisi");
    // printf("%d %s %d\n", st.sid, st.name, st.age);
    struct Student * pst;
    pst = &st;
    pst->sid = 99; //pst->sid 等价于 (*pst).sid 等价于st.sid
    //上式表示pst所指向的结构体变量中的sid这个成员
}

void f(struct Student * pst){
    
    
    pst->age = 11;
    strcpy(pst->name, "aaa");
    pst->sid = 666;
}

//这种方式耗内存 耗时间 不推荐(至少向函数传递了208个字节)  可以改为指针传递
void g(struct Student st){
    
    
    printf("%d %s %d\n", st.sid, st.name, st.age);  
}

void g2(struct Student * st){
    
    
    printf("%d %s %d\n", st->sid, st->name, st->age);
}

//只发送8个字节(64位指针),省时省力
void function2(){
    
    
    struct Student st;
    f(&st);
    // printf("%d %s %d\n", st.sid, st.name, st.age);
    g2(&st);
}

typedef的使用

typedef int element;  //为int重新取一个名字

typedef struct Student{
    
    
    int sid;
    char name[200];
    int age;
}* PST;    //等价于struct Student *

int main(){
    
    
    struct  Student st;
    PST ps = &st;
    
    ps->age = 200;
    printf("%d\n", ps->age);

    return 0;
}

动态分配内存

int * p = (int *)malloc(4);
free(p);
//释放p指向的内存,p本身是静态的,不能手动释放,p本身的内存只能在p变量所在的函数运行终止时由系统自动释放
/*
1.要使用malloc函数,必须添加malloc.h这个头文件
2.malloc函数只有一个形参,并且形参是整型
3. 4表示请求系统为本程序分配4个字节
4.malloc函数只能返回第一个字节的地址
5.12行分配了8个字节,p变量占4个字节,p所指向的内存也占4个字节
6.p本身所占的内存是静态分配的,p指向的内存是动态分配的
*/

猜你喜欢

转载自blog.csdn.net/gary101818/article/details/123195603