啊哈,算法自学记——4th

链表

有一串已经从小到大排好序的数 2 3 5 8 9 10 18 26 32。现需要往这串数中插入 6 使其得到的新序列仍符合从小到大排列。
先回顾下指针:(主要是我不太了解…)

int *p;

指针有什么作用呢?
答案是:存储一个地址。确切地说是存储一个内存空间的地址。严格地说这里的指针 p 也只能存储“一个存放整数的内存空间”的地址,因为在定义的时候我们已经限制了这一点(即定义的时候*p 的前面是 int)。
当然你也可以定义一个只能用来存储“一个存放浮点数的内存空间”的地址,如:

double *p;

整型指针 p 如何才能存储整型变量 a 的地址呢?

p=&a;//&取地址符

有什么用呢?用处就是我们可以用指针 p 来操作变量 a 了。比如我们可以通过操作指针 p 来输出变量 a 的值:

#include <stdio.h>
int main()
{
	int a=10;
	int *p; //定义个指针p
	p=&a; //指针p获取变量a的地址
	printf("%d",*p); //输出指针p所指向的内存中的值
	return 0;
}

MALLOC函数:
malloc 函数的返回类型是 void * 类型。 void * 表示未确定类型的指针。在 C和 C++中, void * 类型可以强制转换为任何其他类型的指针。

#include <stdio.h>
#include <stdlib.h>
int main()
{
	int *p; //定义一个指针p
	p=(int *)malloc(sizeof(int)); //指针p获取动态分配的内存空间地址
	*p=10; //向指针p所指向的内存空间中存入10
	printf("%d",*p); //输出指针p所指向的内存中的值
	return 0;
}

链表每一个结点都由两个部分组成。
左边的部分用来存放具体的数值,那么用一个整型变量就可以;
右边的部分需要存储下一个结点的地址,可以用指针来实现

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

//创建一个结构体来表示链表的结点类型
typedef struct node
{
    int data;
    struct node *next;  
};


int main(int argc, char const *argv[])
{
    struct node *head,*p,*q,*t;
    int n,a;

    printf("Input the num of data:\r\n");
    scanf("%d",&n);

    head=NULL;//头指针指向空

    printf("Input the data:\r\n");
    for (int i = 0; i < n; i++)
    {
        scanf("%d",&a);
        //动态申请一个内存,用来存放一个结点,并用临时指针指向这个结点
        p=(struct node *)malloc(sizeof(struct node));
        /*->叫做结构体指针运算符,也是用来访问结构体内部成员的。
        因为此处 p 是一个指针,所以不能使用.号访问内部成员,而要使用->*/
        p->data=a;//将数据存储到当前结点的数据域中
        p->next=NULL;//设置当前结点的后继指针为空,也就是当前结点的下一个结点为空
        if (head==NULL)//
        {
            head=p;//如果这是第一个创建的结点,则将头指针指向这个结点
        }
        else
        {
            q->next=p;//如果不是第一个创建的结点,则将上一个结点的后继指针指向当前结点
        }
        q=p;//指针q也指向当前结点    
    }
    
    /*插入新的结点*/
    printf("Input the num of insert:\r\n");
    scanf("%d",&a);

    t=head;//
    while (t!=NULL)//遍历链表
    {
        if(t->next->data > a)//如果下一个结点的数据大于插入的数
        {
            p=(struct node *)malloc(sizeof(struct node));//动态申请
            p->data=a;//存入数据
            p->next=t->next;//新增指针的后继指针指向当前结点的后继指针所指向的结点。。。晕
            t->next=p;//当前结点的后继指针指向新增结点
            break;//插入完毕,退出

        }
        t=t->next;
    }
    

    //输出链表中的所有数
    t=head;
    while (t!=NULL)
    {
        printf("  %d",t->data);
        t=t->next;
    }
    free(p);
    return 0;
}

在这里插入图片描述

**

模拟链表:

**
如果你觉得上一节的代码简直是天书,或者你压根就很讨厌指针这些东西,没关系!链表还有另外一种使用数组来实现的方式,叫做模拟链表,我们一起来看看

链表中的每一个结点只有两个部分。我们可以用一个数组 data 来存储每序列中的每一个数。那每一个数右边的数是谁,这一点该怎么解决呢?上一节中是使用指针来解决的,这里我们只需再用一个数组 right 来存放序列中每一个数右边的数是谁就可以了,具体怎么做呢?

在这里插入图片描述
上图的两个数组中,第一个整型数组 data 是用来存放序列中具体数字的,另外一个整型数组 right 是用来存放当前序列中每一个元素右边的元素在数组 data 中位置的。例如 right[1]的值为 2,就表示当前序列中 1 号元素右边的元素存放在 data[2]中;如果是 0,例如right[9]的值为 0,就表示当前序列中 9 号元素的右边没有元素。

看完就晕了,说说我自己的理解:
简单来说就是把上面链表的地址用数组下标来表示,用另一个数组来存放数据数组中的数对应的下标,也即链表中的后继指针,有理解错的地方,希望有心人能给我指正下。

#include <stdio.h>

int main(int argc, char const *argv[])
{
    int data[101],right[101];
    int n,t,len;
    printf("Input the num of data:\r\n");
    scanf("%d",&n);

    printf("Input the data:\r\n");
    for (int i = 1; i <= n; i++)
    {
        scanf("%d",&data[i]);
    }
    len=n;
    //初始化right数组
    for (int i = 1; i <= n; i++)
    {
        if(i!=n)
            right[i]=i+1;
        else
            right[i]=0;
    }
    
    //在data末尾加上一个数据
    len++;
    printf("Input the num of insert:\r\n");
    scanf("%d",&data[len]);

    //从链表的头部开始遍历
    t=1;
    while (t!=0)
    {
       if (data[right[t]]>data[len])//如果当前结点的下一个结点的数大于待插入的数据,将数插入到中间
       {
            right[len]=right[t];//新插入数的下一个结点标号等于当前结点的下一个编号
            right[t]=len;//当前结点的下一个节点编号就是新插入书的结点编号
            break;
       }
       t=right[t];
    }
    //打印
    t=1;
    while (t!=0)
    {
        printf(" %d",data[t]);
        t=right[t];
    }
    
    return 0;
}

在这里插入图片描述

算法,4th,塞奇威克,中文版 《图灵程序设计丛书:算法(第4版)》编辑推荐:Sedgewick之巨著,与高德纳TAOCP一脉相承,几十年多次修订,经久不衰的畅销书,涵盖所有程序员必须掌握的50种算法。《图灵程序设计丛书:算法(第4版)》全面讲述算法和数据结构的必备知识,具有以下几大特色: 算法领域的经典参考书:Sedgewick畅销著作的最新版,反映了经过几十年演化而成的算法核心知识体系。 内容全面:全面论述排序、搜索、图处理和字符串处理的算法和数据结构,涵盖每位程序员应知应会的50种算法。 全新修订的代码:全新的Java实现代码,采用模块化的编程风格,所有代码均可供读者使用。 与实际应用相结合:在重要的科学、工程和商业应用环境下探讨算法,给出了算法的实际代码,而非同类著作常用的伪代码。 富于智力趣味性:简明扼要的内容,用丰富的视觉元素展示的示例,精心设计的代码,详尽的历史和科学背景知识,各种难度的练习,这一切都将使读者手不释卷。 科学的方法:用合适的数学模型精确地讨论算法性能,这些模型是在真实环境中得到验证的。 与网络相结合:配套网站algs4.cs.princeton.edu提供了本书内容的摘要及相关的代码、测试数据、编程练习、教学课件等资源。 作者简介 作者:(美国)塞奇威克(Robert Sedgewick)^韦恩(Kevin Wayne) 译者:谢路云 塞奇威克(Robert Sedgewick),斯坦福大学博士,导师为Donald E. Knuth,从1985年开始一直担任普林斯顿大学计算机科学系教授,曾任该系主任,也是Adobe Systems公司董事会成员,曾在Xerox PARC、国防分析研究所(institute for Defense Analyses)和法国国家信息与自动化研究所(INRIA)从事研究工作。他的研究方向包括解析组合学、数据结构和算法的分析与设计、程序可视化等。 韦恩(Kevin Wayne),康奈尔大学博士,普林斯顿大学计算机科学系高级讲师,研究方向包括算法的设计、分析和实现,特别是图和离散优化。 目录 第1章 基础 1.1 基础编程模型 1.1.1 Java程序的基本结构 1.1.2 原始数据类型与表达式 1.1.3 语句 1.1.4 简便法 1.1.5 数组 1.1.6 静态方法 1.1.7 API 1.1.8 字符串 1.1.9 输入输出 1.1.10 二分查找 1.1.11 展望 1.2 数据抽象 1.2.1 使用抽象数据类型 1.2.2 抽象数据类型举例 1.2.3 抽象数据类型的实现 1.2.4 更多抽象数据类型的实现 1.2.5 数据类型的设计 1.3 背包、队列和栈 1.3.1 API 1.3.2 集合类数据类型的实现 1.3.3 链表 1.3.4 综述 1.4 算法分析 1.4.1 科学方法 1.4.2 观察 1.4.3 数学模型 1.4.4 增长数量级的分类 1.4.5 设计更快的算法 1.4.6 倍率实验 1.4.7 注意事项 1.4.8 处理对于输入的依赖 1.4.9 内存 1.4.10 展望 1.5 案例研究:union—find算法 1.5.1 动态连通性 1.5.2 实现 1.5.3 展望 第2章 排序 2.1 初级排序算法 2.1.1 游戏规则 2.1.2 选择排序 2.1.3 插入排序 2.1.4 排序算法的可视化 2.1.5 比较两种排序算法 2.1.6 希尔排序 2.2 归并排序 2.2.1 原地归并的抽象方法 2.2.2 自顶向下的归并排序 2.2.3 自底向上的归并排序 2.2.4 排序算法的复杂度 2.3 快速排序 2.3.1 基本算法 2.3.2 性能特点 2.3.3 算法改进 2.4 优先队列 2.4.1 API 2.4.2 初级实现 2.4.3 堆的定义 2.4.4 堆的算法 2.4.5 堆排序 2.5 应用 2.5.1 将各种数据排序 2.5.2 我应该使用哪种排序算法 2.5.3 问题的归约 2.5.4 排序应用一览 第3章 查找 3.1 符号表 3.1.1 API 3.1.2 有序符号表 3.1.3 用例举例 3.1.4 无序链表中的顺序查找 3.1.5 有序数组中的二分查找 3.1.6 对二分查找的分析 3.1.7 预览 3.2 二叉查找树 3.2.1 基本实现 3.2.2 分析 3.2.3 有序性相关的方法与删除操作 3.3 平衡查找树 3.3.12—3查找树 3.3.2 红黑二叉查找树 3.3.3 实现 3.3.4 删除操作 3.3.5 红黑树的性质
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值