• 【C语言篇】☞ 10. 数组、常见算法、模拟栈操作


    数组

    1. 概念:

      一组具有相同数据类型的数据的有序集合。

    数组名是一个地址(是常量),不可改变、不能赋值、不能做左值。

    int a[3]; // 定义了一个名称叫做a的数组, 数组中可以存放3int类型的数据

    2. 初始化

      1int a[5]={1,2,3,4,5};     //常用

      2int a[5]={1,2,3};         //部分初始化,剩余的元素初始化为0,即{12300}

      3int a[5]={[0]=1,[1]=2,[3]=4,5}; //指定初始化,即{12045}

      4int a[5]={0};             //常用,初始化清零 {00000}

      5int a[5]={};              //不推荐(可读性不好) {00000}

      6int a[]={1,2,3,4,5};      //不推荐 省略数组的长度,可读性不好

    3. 数组长度

      数组的长度 = size(数组) / size(元素)

        // 动态计算数组的元素个数

        int a[5];

        int length = sizeof(a) / sizeof(a[0]);

      数组名a是空间的首地址: a == &a[0] == &a

      

      

    • 问题一:为什么在函数形式参数的声明中*a与a[]是一样的?

      上述两种形式都说明我们期望的实际参数是指针。在这两种情况下,对a可进行的运算是相同的。而且,可在函数体内给a本身赋予新的值。C语言要求数组名只能用作“常量指针”,但对于数组型形式参数的名字没有这一限制。

    • 问题二:请问i[a]与a[i]是一样的吗?

      是的,它们是一样的。对于编译器而言,i[a]等同于*(i+a),而*(i+a)也就是*(a+i),我们知道,*(a+i)等效于a[i]。虽然,i[a]与a[i]是等效的,但一般我们在编程中只使用a[i],很少使用到i[a]。   

    • 问题三:数组下标越界的后果是什么?

      C语言中数组下标越界,编译器是不会检查出错误的,但是实际上后果可能会很严重,比如程序崩溃、程序的其他数据被改变等,所以在日常的编程中,程序员应当养成良好的编程习惯,避免这样的错误发生。

    /**

     *  数组的拷贝

     */

    #include <stdio.h>

    int main() {

        int a[5] = {1, 2, 3, 4, 5};

        int b[5];

        for (int i = 0; i < 5; i++) {

            b[i] = a[i];

            printf("%d ", b[i]);

        }

        printf(" ");

        return 0;

    }

    二维数组

    1. 概念

      二维数组其实是数组的数组,可以将二维数组理解为一张几行几列的二维表。

    二维数组: 数组中的每一个元素又是一个数组, 那么这个数组就称之为二维数组

    元素类型 数组名称[一维数组的个数][每个一维数组的元素个数];

    元素类型 数组名称[行数][列数];

     

    //明确的告诉二维数组: 2个一维数组,每个一维数组有3个元素

    // {{1, 2, 3}, {4, 5, 6}};

    int a[2][3] = {1, 2, 3, 4, 5, 6};

    数组的名称就是数组的地址(首地址): &a == a == &a[0]

    2. 初始化

    int a[3][2]={{1,2},{3,4},{5,6}}; //对应元素内容

    int b[3][2]={1,2,3,4,5,6}; //整体赋值

    int c[3][2]={1,2,3,4}; //依次赋值,自动补零

     

    注意点: 每个一维数组的元素个数不能省略

    char name[][2] = {'r', 'b', 'h', 'j'};//OK

    // 搞不清楚应该分配多大的存储空间, 以及搞不清楚应该把哪些数据赋值给第一个数组, 以及哪些数据赋值给第二个数组

    char name1[2][] = {'r', 'b', 'h', 'j'};//ERROR

    char name2[2][2] = {'r', 'b', 'h', 'j'};//OK

     

       return;结束当前函数!

    exit(0);退出整个程序。(包含<stdlib.h>头文件)

      注:数组作为函数的参数传递(是地址传递), 在函数内部修改形参的值会影响到函数外部的实参值。

    常见的一些算法

      选择排序、冒泡排序、折半查询(二分查找)、查表法(转进制)

    三种排序算法可以总结为如下:

    • 都将数组分为已排序部分和未排序部分。
    • 选择排序将已排序部分定义在左端,然后选择未排序部分的最小元素和未排序部分的第一个元素交换。
    • 冒泡排序将已排序部分定义在右端,在遍历未排序部分的过程执行交换,将最大元素交换到最右端。
    • 插入排序将已排序部分定义在左端,将未排序部分元的第一个元素插入到已排序部分合适的位置。

    1. 选择排序

      第1趟:在n个数中找到最小(大)数与第一个数交换位置

      第2趟:在剩下n-1个数中找到最小(大)数与第二个数交换位置

          重复这样的操作...依次与第三个、第四个...数交换位置

      第n-1趟,最终可实现数据的升序(降序)排列。

      选择排序的特点:最值出现在起始端

       

    2. 冒泡排序

      第1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n个元素位置(两两比较n-1次)

      第2趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第n-1个元素位置(两两比较n-2次)

          ……      ……

      第n-1趟:依次比较相邻的两个数,不断交换(小数放前,大数放后)逐个推进,最值最后出现在第2个元素位置(两两比较1次)

      冒泡排序的特点:相邻元素两两比较,比较完一趟,最值出现在末尾

      

    3. 折半查找

      优化查找时间(不用遍历全部数据)

    折半查找的原理:

        1> 数组必须是有序的

        2> 必须已知minmax(知道范围)

        3> 动态计算mid的值,取出mid对应的值进行比较

        4> 如果mid对应的值大于要查找的值,那么max要变小为mid-1

        5> 如果mid对应的值小于要查找的值,那么min要变大为mid+1

    /**

     *  已知一个有序数组, 和一个key, 要求从数组中找到key对应的索引位置

     */

    #include <stdio.h>

    #include <time.h>

    //1:遍历数组逐个查找

    int findKey(int *arr, int length, int key) {

        for (int i = 0; i < length; i++) {

            if (arr[i] == key) {

                return i;

            }

        }

        return -1;

    }

    //2:折半查找

    int findKey1(int *arr, int length,int key) {

        int min = 0, max = length - 1, mid;

        while (min <= max) {

            mid = (min + max) / 2; //计算中间值

            if (key > arr[mid]) {

                min = mid + 1;

            } else if (key < arr[mid]) {

                max = mid - 1;

            } else {

                return mid;

            }

        }

        return -1;

    }

    int main() {

        int arr[10000] = {0, [9995] = 1, 3, 6, 10, 12};

        int key = 3;

        int length = sizeof(arr) / sizeof(arr[0]);

        clock_t startTime = clock();

    //    int index = findKey(arr, length, key);//共消耗了27毫秒!

        int index = findKey1(arr, length, key); //共消耗了1毫秒!

        clock_t endTime = clock();

        printf("共消耗了%lu毫秒! ", endTime - startTime);

        printf("key对应的下标位置: %d ", index); // 9996

        return 0;

    }

      

    /**

     *  已知一个有序的数组, 要求给定一个数字, 将该数字插入到数组中, 使数组还是有序的

     *  分析:其实就是找到需要插入数字的位置(min的位置)

     */

    #include <stdio.h>

     

    int insertValue(int *arr, int length,int key) {

        int min = 0, max = length - 1, mid;

        while (min <= max) {

            mid = (min + max) / 2; //计算中间值

            if (key > arr[mid]) {

                min = mid + 1;

            }

            if (key < arr[mid]) {

                max = mid - 1;

            }

        }

        return min;

    }

    int main() {

        int a[5] = {1, 3, 5, 7, 9};

        int key = 4;

        int length = sizeof(a) / sizeof(a[0]);

        int insertIndex = insertValue(a, length, key);

        printf("需要插入的位置是: %i ", insertIndex);

        return 0;

    }

    4. 进制查表法

      可以打印输出任意进制

      

      

      

    封装优化 进制查表法:

    /**

     * 查表法(封装优化)

     */

    #include <stdio.h>

    /**

     *  转换所有的进制

     *  value:  就是需要转换的数值

     *  base:   就是需要&上的数

     *  offset: 就是需要右移的位数

     */

    void total(int value, int base, int offset)

    {

        // 1.定义一个数组, 用于保存十六进制中所有的取值

        char charValue[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

        // 2.定义一个数组, 用于保存查询后的结果

        char result[32] = {'0'};

        // 3.定义一个变量, 用于记录当前需要存储到查询结果数组的索引

        int index = sizeof(result) / sizeof(result[0]);

        // 4.循环取值

        while (value != 0) {

            // 1) 取出1位的值

            int res = value & base;// base: 1 7 15

            // 2) 利用取出来得值到表中查询对应的结果

            char ch = charValue[res];

            // 3) 存储查询的结果

            result[--index] = ch;

            // 4) 移除二进制被取过的1

            value = value >> offset;// offset: 1 3 4

        }

        // 5.打印结果

        for (int i = index; i < 32; i++) {

            printf("%c", result[i]);

        }

        printf(" "); 

    }

    /** 转二进制 */

    void printBinary(int num) {

        total(num, 1, 1);

    }

    /** 转八进制 */

    void printOct(int num) {

        total(num, 7, 3);

    }

    /** 转十六进制 */

    void printHex(int num) {

        total(num, 15, 4);

    }

    int main() {

        printBinary(10); //1010

        printOct(23);    //27

        printHex(1004);  //3ec

        return 0;

    }

      

       

    模拟栈的操作

      

      

     

     

  • 相关阅读:
    自制404页面
    Http错误代码含义
    数据访问基础类(基于Access数据库)
    NTILE函数在SQL Server 2000中的实现方法
    矩阵螺旋输出
    分区排名方案和排名值效率分析【图文+测试代码】
    安装eclipse axis2 插件(links 方式)
    perforce关闭服务后无法重启
    A4纸网页打印中对应像素的设定和换算
    2005数据库脚本在SQL2000上执行
  • 原文地址:https://www.cnblogs.com/bossren/p/6406234.html
Copyright © 2020-2023  润新知