數據結構與算法入門C語言 (一) 概述


筆記來源於郝斌老師數據結構與算法視頻,博主學習後 純手打,侵刪。

數據結構概述

一、數據結構定義

如何把現實中大量而複雜的問題以特定的數據類型特定的存儲結構保存到主存儲器(內存)中,以及在此基礎上爲實現某個功能(比如查找、刪除某個元素,對所有元素排序)而執行的相應操作,這個操作也叫算法。
特定的數據類型: 個體元素
特定的存儲結構:個體和個體之間的關係

數據結構=個體+個體的關係
算法=對存儲數據的操作

二、算法定義

算法:解題的方法和步驟

三、 衡量算法的標準

1.時間複雜度
大概程序執行的次數,而非執行時間。
2.空間複雜度
算法執行過程中大概所佔用的最大內存。
3.難易程度
4.健壯性

四、 數據結構的地位

數據結構是軟件心中最核心的課程

程序=數據結構+數據的操作+可以被計算機執行的語言

1.什麼是堆?什麼是棧?
很多人以爲是內存裏的2塊兒空間,一塊兒叫堆,一塊叫棧,其實不對。實際上是指內存分配的方法不同的2種方式。如果是壓棧出棧的方式分配和釋放的內存就叫棧內存。如果是以堆排序分配的內存就叫堆內存。

**注意:數據結構裏是沒有堆這個概念的,堆是什麼?是分配內存的一種方式,而不是存儲數據的一種結構。

2.函數調用,如何調用呢?

壓棧和出棧。

  • 按時間存儲的東西得有個順序吧,按順序存儲的結構就是隊列
  • 編譯原理得學樹。
  • 數據庫就是數據結構得簡化版,討論得問題差不多,解決得問題更狹窄了
  • 程序=數據的存儲+數據的操作+可被計算機執行的語言。
  • 數據結構很重要,難度大,學完很難做出東西來,學它是練內功。

五、預備知識

1.指針

  • 指針的重要性
    指針是C語言的靈魂

  • 定義

    • 地址
      內存單元的編號
      從0開始的非負整數
      範圍:0-FFFFFFFF【0~4G-1】
  • 指針
    指針就是地址,地址就是指針。
    指針的本質是一個操作受限的非負整數

  • 指針變量
    指針變量是存放內存單元地址(編號)的變量

  • 指針的分類
    1.基本類型的指針

#include <stdio.h>

int main(void)
{
    int *p; //p是個變量名字,int * 表示該P變量只能存儲int類型變量的地址
    int i = 10;
    int j;

    //(1)❌此時p還沒有被賦值,裏面是個垃圾值,這個垃圾值很可能正好是某個變量的地址
    //所以應該在使用 *p 之前給p賦值地址:p = &i;
    j = *p;

    //(2)❌ 給垃圾值地址的變量賦值一個新值,垃圾值應不受你控制的,隨意改內存很危險 。
    //若是 先寫了p = &i; 則下面等價於 i = i;
    *p = i;
}
#include <stdio.h>

change_arr(int *p, int len)
{
    p[0] = -1; //p[0] = *(p + 0) = *p
    p[2] = -9; //p[2] = *(p + 2) = *(a + 2) =     	a[2]
}

int main(void)
{
    int a[5] = {1, 2, 3, 4, 5};
    printf("%d\n", a);
    printf("%d\n", a + 1); //#####70      int型佔4個字節
    printf("%d\n", a + 2); //#####74
    printf("%d\n", a + 3); //#####78
    a + n = addr(a) + n * sizeof(typeof(a))
                              change_arr(a, 5);
    printf("%d\n", a[0]); // -1
    printf("%d\n", a[2]); //-9
}

所有的指針變量只佔4個字節,用第一個字節的地址表示整個變量的地址

#include <stdio.h>
int main(void)
{
    doueble arr[5] = {1.1, 2.2, 3.3, 4.4, 5.5};
    double *p = &arr[0];
    printf("%p\n", p); // %p以16進制的形式輸出地址(012FF5C)

    p = &arr[1];
    printf("%p\n", p); // %p以16進制的形式輸出地址(012FF64)64-5C=8字節
    //int 類型的話差4個,因爲int 是4字節長度
    return 0;
}

如何通過函數修改實參的值

無論要通過函數改寫什麼類型的值,只需要傳遞它的地址就行了。

#include <stdio.h>
int main(void)
{
    int i = 10;
    int *p = &i; // int * p; p = & i;
    printf("%p\n", p);
    f(&p);
    printf("%p\n", p);
    return 0;
}

void f(int *q)
{
    *q = (int *)0 x FFFFFFFF;
}

結構體使用概述

  • 1.爲什麼需要結構體

爲了表示一些複雜的數據,而普通的基本類型變量無法滿足需求。

  • 2.什麼叫結構體

結構體是用戶根據實際需要自己定義的複合數據類型。結構體給人感覺模擬事物模擬的不徹底(沒有辦法)但是仍然有好處;以算法爲核心,血手結構。算法在面向過程的結構語言裏最好。而面向對象的語言算法就不是其核心了。

  • 3.如何使用結構體

    兩種方式:
               struct Student st  = {1000,"LeeQiang",20};
               struct Student  * pst  = &st;
    
#include<stdio.h> struct Student
{
    int sid;
    char name[20];
    int age;
};
int min(void)
{
    struct Student st = {1,“LeeQiang” , 20};
    printf("%d %s %d\n", st.sid, st.name, st.age);
    //第一種訪問方式
    st.sid = 99;
    // st.name = "LeeSi" // ❌error
    strcpy(st.name, "LeeSi");
    st.age = 28;
    printf("%d %s %d\n", st.sid, st.name, st.age);

    struct Student *pst; //第二種方式最常用
    pst = &st;
    pst->age = 22; // pst->age等價於 (*pst).age 而(*pst).age=st.age 所以pst->age = st.age;
} //這種方式好,因爲只需要4個字節的參數變量

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

結構體的2種變量方式
1.st.age
2.pst ->age //pst 所指向的結構體變量中的age這個成員;

注意事項
1.結構體變量不能相互之間加減乘除,但能相互賦值。
2.普通結構體變量和結構體變量指針變量作爲函數傳參問題。

動態內存的分配和釋放

假設構造一個 int 型的一維數組
int len; int * pArr = (int *)malloc(sizeof(int *)len)

  • 本語句分配了兩塊內存,一塊內存是動態分配的的總共len個字節;另一個是靜態分配的,是pArr變量本身所佔的內存總共4個字節。
  • malloc 只有一個 int 型的 形參,表示要求系統分配的字節數。
  • malloc 函數的功能是請求系統分配 len個字節的內存空間,如果分配成功,則返回第一個字節。如果返回不成功,則返回NULL。
  • malloc 函數能且只能返回第一個字節的地址,所以我們需要把這個無任何意義的第一個字節的地址(俗稱乾地址)轉化爲一個有實際意義的地址,因此,malloc函數前面必須加強制類型轉換(數據類型*),表示把這個無實際意義的第一個字節的地址轉化爲相應類型的地址。
  • free(*pArr)表示把所指向的內存釋放掉。pArr本身的內存是靜態的,不能由程序員手動釋放,只能在pArr變量所在的函數運行終止時有系統自動釋放。
  • 跨函數使用內存
    • 靜態內存不可跨函數使用。

    • 靜態內存在函數執行期間可以被其他函數所使用。

    • 靜態內存在函數執行完畢之後就不能在被其他函數使用。

    • 動態內存可以跨函數使用

    • 動態內存在函數執行完畢之後仍可以被其他函數使用,除非使用free()方法動態分配的內存

#include <malloc.h>
#include <stdio.h>
int main(void)
{
    int a[5] = {4, 10, 2, 8, 6};
    int len;
    printf("請輸入需要分配的數組長度:len = ");
    scanf("%d", &len);
    int *pArr = (int *)malloc(szieof(int *) len);
    //* pArr = 4; //類似於 a[0] = 4;
    // pArr[1] = 10; //類似於 a[1] = 10;
    // printf("%d%d\n",*pArr,pArr[1]);
    //我們可以把pARR 當做一個普通數組來使用
    for (int i = 0; i < len; ++i)
        scanf("%d", &pArr[i]);
    for (i = 0; i < len; ++i;)
        printf("%d\n", *(pArr + i));
    free(pArr); // 把pArr所代表的動態分配的20個字節的內存釋放
    return 0}
#include <stdio.h>
struct Student
{
    int sid;
    int age;
};

struct Student *CreateStudent(void);
void ShowStudent(struct Student *);

int main(void)
{
    struct Student *pS;
    pS = CreateStudent();
    ShowStudent(ps);
    return 0;
}

void ShowStudent(struct Student *pS)
{
    printf("%d%d\n", pst->sid, pst->age);
}

struct Student *CreateStudent(void)
{
    struct Student *p = (struct Student *)malloc(sizeof(struct Student));
    p->sid = 99;
    p->age = 88;
    return p;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章