數據結構與算法【基礎】+線性結構部分

一、數據結構核心名詞

  • 數據

程序的操作對象,用于描述客觀事物
特點:
1、可以輸入到計算機
2、可以被計算機運行

  • 數據對象

性質相同的數據元素的集合(類似于數組)

  • 數據元素

組成數據的對象的基本單位

  • 數據項

一個數據元素由若干個數據項組成

  • 結構

數據元素之間不是獨立的,存在特定的關系,這些關系即是結構

  • 數據結構

指的是數據對象中的數據元素之間的關系

代碼中的體現:

#include <stdio.h>
//聲明一個結構體類型
struct Person{      //一種數據結構
    char *name;     //數據項--名字
    char *title;    //數據項--職稱
    int  age;       //數據項--年齡
};
int main(int argc, const char * argv[]) {
    struct Person p1;     //數據元素;
    struct Person tArray[10]; //數據對象;
    p1.age = 25;       //數據項
    p1.name = "小C";    //數據項
    p1.title = "程序猿";  //數據項
    return 0;
}
數據結構-基本數據單位.png

二、邏輯結構與物理結構區別

邏輯結構(數據和數據之間的邏輯關系)

  • 集合結構

所有的數據都屬于同一集合
??:同學A、B、C、D都屬于一班,他們之間沒有先后順序的,是平等的。

  • 線性結構

數據和數據是 一對一
??:數組、線性表、字符串(由n個字符組成)、棧(先進后出)、隊列(先進先出)

  • 樹形結構

數據和數據是 一對多

  • 圖形結構

數據和數據是 多對多

物理結構(數據最終存儲在內存當中)

  • 順序存儲結構

開辟一段連續的內存空間,然后依次存儲進去

  • 鏈式存儲結構

不需要提前開辟一個連續的內存空間

順序存儲結構與鏈式存儲結構的區別與優缺點

區別
1、鏈式存儲結構的地址不一定是連續的,但順序存儲結構的內存地址一定是連續的;
2、鏈式存儲結構適用于比較頻繁地插入、刪除、更新元素,而順序存儲結構適用于頻繁的查詢時使用

優缺點
1、空間上:順序比鏈式節約空間
2、存儲操作上:順序支持隨機存儲,方便操作
3、插入和刪除上:鏈式的要比順序的方便(因為插入的話順序也很方便,問題是順序的插入要執行更大的空間復雜度,包括一個從表頭索引以及索引后的元素后移,而鏈式是索引后,插入就完成了)

三、算法設計要求

算法 就是解決特定問題求解的描述,在計算機中表現為指令的有限序列,并且每個指令表示一個或多個操作

算法的特性

  • 輸入輸出
  • 有窮性
  • 確定性
  • 可行性

設計要求即衡量算法的要求

  • 正確性
  • 可讀性
  • 健壯性
  • 時間效率高和儲存量低

四、算法時間復雜度

大O表示法

1、用常數1取代運行時間中所有常數
??:3 -> O(1)
2、在修改運行次數函數中,只保留最高階層
??:n3+2n2+5 -> O(n3)
3、如果在最高階存在且不等于1,則去除這個項目相乘的常數
??:2n3 -> O(n3)

下面我們拿些??看看:

4.1、常數階,時間復雜度為 O(1)

//1+1+1=3  O(1)
void testSum1(int n){
    int sum = 0;                //執行1次
    sum = (1+n)*n/2;            //執行1次
    printf("testSum1:%d\n",sum);//執行1次
}
//1+1+1+1+1+1+1=7  O(1)
void testSum2(int n){
    int sum = 0;                //執行1次
    sum = (1+n)*n/2;            //執行1次
    sum = (1+n)*n/2;            //執行1次
    sum = (1+n)*n/2;            //執行1次
    sum = (1+n)*n/2;            //執行1次
    sum = (1+n)*n/2;            //執行1次
    printf("testSum2:%d\n",sum);//執行1次
}

4.2、線性階

//n+n=2n   O(n)
void add2(int x,int n){
    for (int i = 0; i < n; i++) {  //執行n次
        x = x+1;                   //執行n次
    }
}
//1+n+1+n+1=2n+3   O(n)
void testSum3(int n){
    int i,sum = 0;               //執行1次
    for (i = 1; i <= n; i++) {   //執行n+1次
        sum += i;                //執行n次
    }
    printf("testSum3:%d\n",sum);  //執行1次
}

4.3、對數階

//log2 n+1     O(logn)
void testA(int n){
    int count = 1;         //執行1次
    while (count < n) {    
        count = count * 2; //2的x次方等于n  
                            //x=log2 n次
    }
}

4.4、平方階

//n+n^2+n^2=2n^2+n    O(n^2)
void add3(int x,int n){
    for (int i = 0; i< n; i++) {      //執行n次
        for (int j = 0; j < n ; j++) {//執行n*n次
            x=x+1;   //執行n*n次
        }
    }
}
//n+(n-1)+(n-2)+...+1 = n(n-1)/2 = n^2/2 + n/2 
//等差數列公式:sn=n(a1+an)/2
//O(n^2)
void testSum4(int n){
    int sum = 0;  
    for(int i = 0; i < n;i++)  
        for (int j = i; j < n; j++) {
            sum += j;
        }
}
//1+(n+1)+n(n+1)+n^2+n^2=2+3n^2+2n
//O(n^2)
void testSum5(int n){
    int i,j,x=0,sum = 0;           //執行1次
    for (i = 1; i <= n; i++) {     //執行n+1次
        for (j = 1; j <= n; j++) { //執行n(n+1)
            x++;                   //執行n*n次
            sum = sum + x;         //執行n*n次
        }
    }
}

4.5、立方階

//1+n+n^2+n^3+n^3=2n^3+n^2+n+1
//O(n^3)
void testB(int n){
    int sum = 1;                         //執行1次
    for (int i = 0; i < n; i++) {        //執行n次
        for (int j = 0 ; j < n; j++) {   //執行n*n次
            for (int k = 0; k < n; k++) {//執行n*n*n次
                sum = sum * 2;          //執行n*n*n次
            }
        }
    }
}

常見的時間復雜度

執行次數函數 術語
12 O(1) 常數階
2n+3 O(n) 線性階
3n2+2n+1 O(n2) 平方階
5log2n+20 O(logn) 對數階
2n+3nlog2 n+19 O(nlogn) nlogn階
6n3+2n2+3n+4 O(n3) 立方階
2n O(2n) 指數階

O(1) < O(log n) < O(n) < O(nlog n) < O(n2) < O(n3) < O(2n)

五、算法空間復雜度計算

主要考慮算法執行時所需要額輔助空間
計算公式:S(n)=n(f(n))
其中,n為問題的規模,f(n)為語句關于n所占存儲空間的函數

問題:數組逆序,將一維數組a中的n個數逆序存放在愿數組中。

#include <stdio.h>
int main(int argc, const char * argv[]) {
    // insert code here...
    int n = 5;
    int a[10] = {1,2,3,4,5,6,7,8,9,10};
    //算法實現(1)  空間復雜度為:O(1)
    //只使用到一個輔助
    int temp;
    for(int i = 0; i < n/2 ; i++){
        temp = a[i];
        a[i] = a[n-i-1];
        a[n-i-1] = temp;
    }
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);
    }
    
    //算法實現(2)   空間復雜度為:O(n)
    int b[10] = {0};
    for(int i = 0; i < n;i++){
        b[i] = a[n-i-1];
    }
    for(int i = 0; i < n; i++){
        a[i] = b[i];
    }
    for(int i = 0;i < 10;i++)
    {
        printf("%d\n",a[i]);   
    }
    return 0;
}

衡量一個算法的時候是描述最壞的情況,如果最壞情況一致,那么再比較平均值

六、線性表----關于順序存儲的實現(增刪改查)

6.1、順序存儲(邏輯相鄰,物理存儲地址相鄰)

6.1.1、順序表初始化

#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OK 1
#define MAXSIZE 20 /* 存儲空間初始分配量 */

typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
typedef int ElemType;/* ElemType類型根據實際情況而定,這里假設為int */

//定義結點
typedef struct{
    ElemType data;
    struct Node *next;
}sqlist;
//1.初始化
Status InitList(sqlist *L){
    L->data = (sqlist)malloc(sizeof(ElemType)*MAXSIZE);
   if(!L->data) return ERROR;
    L->length=0;
    return OK;
}

6.1.2、順序表的插入

/*
 初始條件:順序線性表L已存在,1≤i≤ListLength(L);
 操作結果:在L中第i個位置之前插入新的數據元素e,L的長度加1
 */
Status ListInsert(Sqlist *L,int i,ElemType e){
    
    //i值不合法判斷
    if((i<1) || (i>L->length+1)) return ERROR;
    //存儲空間已滿
    if(L->length == MAXSIZE) return ERROR;
 
    //插入數據不在表尾,則先移動出空余位置
    if(i <= L->length){
        for(int j = L->length-1; j>=i-1;j--){
       
            //插入位置以及之后的位置后移動1位
            L->data[j+1] = L->data[j];
        }
    }
    //將新元素e 放入第i個位置上
    L->data[i-1] = e;
    //長度+1;
    ++L->length;    
    return OK;
}

6.1.3、順序表的取值

Status GetElem(Sqlist L,int i, ElemType *e){
    //判斷i值是否合理, 若不合理,返回ERROR
    if(i<1 || i > L.length) return  ERROR;
    //data[i-1]單元存儲第i個數據元素.
    *e = L.data[i-1];
    return OK;
}

6.1.4、順序表刪除

/*
 初始條件:順序線性表L已存在,1≤i≤ListLength(L)
 操作結果: 刪除L的第i個數據元素,L的長度減1
 */
Status ListDelete(Sqlist *L,int i){
    
    //線性表為空
    if(L->length == 0) return ERROR;
    
    //i值不合法判斷
    if((i<1) || (i>L->length+1)) return ERROR;
    
    for(int j = i; j < L->length;j++){
        //被刪除元素之后的元素向前移動
        L->data[j-1] = L->data[j];
    }
    //表長度-1;
    L->length --;
    return OK;
}

6.1.5、清空順序表

/* 初始條件:順序線性表L已存在。操作結果:將L重置為空表 */
Status ClearList(Sqlist *L)
{
    L->length=0;
    return OK;
}

6.1.6、判斷順序表清空

/* 初始條件:順序線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE */
Status ListEmpty(Sqlist L)
{
    if(L.length==0)
        return TRUE;
    else
        return FALSE;
}

6.1.7、獲取順序表長度ListEmpty元素個數

int ListLength(Sqlist L)
{
    return L.length;
}

6.1.8、順序輸出List

/* 初始條件:順序線性表L已存在 */
/* 操作結果:依次對L的每個數據元素輸出 */
Status TraverseList(Sqlist L)
{
    int i;
    for(i=0;i<L.length;i++)
        printf("%d\n",L.data[i]);
    printf("\n");
    return OK;
}

6.1.9、順序表查找元素并返回位置

/* 初始條件:順序線性表L已存在 */
/* 操作結果:返回L中第1個與e滿足關系的數據元素的位序。 */
/* 若這樣的數據元素不存在,則返回值為0 */
int LocateElem(Sqlist L,ElemType e)
{
    int i;
    if (L.length==0) return 0;
    
    for(i=0;i<L.length;i++)
    {
        if (L.data[i]==e)
            break;
    }
    if(i>=L.length) return 0;
    return i+1;
}

6.2、鏈式存儲

6.2.1、初始化單鏈表線性表

#define ERROR 0
#define TRUE 1
#define FALSE 0
#define OK 1

#define MAXSIZE 20 /* 存儲空間初始分配量 */

typedef int Status;/* Status是函數的類型,其值是函數結果狀態代碼,如OK等 */
typedef int ElemType;/* ElemType類型根據實際情況而定,這里假設為int */

//定義結點
typedef struct Node{
    ElemType data;
    struct Node *next;
}Node;

typedef struct Node * LinkList;

Status InitList(LinkList *L){
    
    //產生頭結點,并使用L指向此頭結點
    *L = (LinkList)malloc(sizeof(Node));
    //存儲空間分配失敗
    if(*L == NULL) return ERROR;
    //將頭結點的指針域置空
    (*L)->next = NULL;
    
    return OK;
}

6.2.2、單鏈表插入

/*
 初始條件:順序線性表L已存在,1≤i≤ListLength(L);
 操作結果:在L中第i個位置之后插入新的數據元素e,L的長度加1;
 */
Status ListInsert(LinkList *L,int i,ElemType e){
 
    int j;
    LinkList p,s;
    p = *L;
    j = 1;
    
    //尋找第i-1個結點
    while (p && j<i) {
        p = p->next;
        ++j;
    }
    
    //第i個元素不存在
    if(!p || j>i) return ERROR;
    
    //生成新結點s
    s = (LinkList)malloc(sizeof(Node));
    //將e賦值給s的數值域
    s->data = e;
    //將p的后繼結點賦值給s的后繼
    s->next = p->next;
    //將s賦值給p的后繼
    p->next = s;
    
    return OK;
}

6.2.3、單鏈表取值

/*
 初始條件: 順序線性表L已存在,1≤i≤ListLength(L);
 操作結果:用e返回L中第i個數據元素的值
 */
Status GetElem(LinkList L,int i,ElemType *e){
    
    //j: 計數.
    int j;
    //聲明結點p;
    LinkList p;
    
    //將結點p 指向鏈表L的第一個結點;
    p = L->next;
    //j計算=1;
    j = 1;
    
    
    //p不為空,且計算j不等于i,則循環繼續
    while (p && j<i) {
        
        //p指向下一個結點
        p = p->next;
        ++j;
    }
    
    //如果p為空或者j>i,則返回error
    if(!p || j > i) return ERROR;
    
    //e = p所指的結點的data
    *e = p->data;
    return OK;
}

6.2.4、單鏈表刪除元素

/*
 初始條件:順序線性表L已存在,1≤i≤ListLength(L)
 操作結果:刪除L的第i個數據元素,并用e返回其值,L的長度減1
 */
Status ListDelete(LinkList *L,int i,ElemType *e){
    
    int j;
    LinkList p,q;
    p = (*L)->next;
    j = 1;
    
    //查找第i-1個結點,p指向該結點
    while (p->next && j<(i-1)) {
        p = p->next;
        ++j;
    }
    
    //當i>n 或者 i<1 時,刪除位置不合理
    if (!(p->next) || (j>i-1)) return  ERROR;
    
    //q指向要刪除的結點
    q = p->next;
    //將q的后繼賦值給p的后繼
    p->next = q->next;
    //將q結點中的數據給e
    *e = q->data;
    //讓系統回收此結點,釋放內存;
    free(q);
    
    return OK;
}

6.2.5、單鏈表前插入法

/* 隨機產生n個元素值,建立帶表頭結點的單鏈線性表L(前插法)*/
void CreateListHead(LinkList *L, int n){
    
    LinkList p;
    
    //建立1個帶頭結點的單鏈表
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    
    //循環前插入隨機數據
    for(int i = 0; i < n;i++)
    {
        //生成新結點
        p = (LinkList)malloc(sizeof(Node));
       
        //i賦值給新結點的data
        p->data = i;
        //p->next = 頭結點的L->next
        p->next = (*L)->next;
        
        //將結點P插入到頭結點之后;
        (*L)->next = p;
        
    }
}

6.2.6、單鏈表后插入法

/* 隨機產生n個元素值,建立帶表頭結點的單鏈線性表L(后插法)*/
void CreateListTail(LinkList *L, int n){
    
    LinkList p,r;
 
    //建立1個帶頭結點的單鏈表
    *L = (LinkList)malloc(sizeof(Node));
    //r指向尾部的結點
    r = *L;
    
    for (int i=0; i<n; i++) {
        
        //生成新結點
        p = (Node *)malloc(sizeof(Node));
        p->data = i;
        
        //將表尾終端結點的指針指向新結點
        r->next = p;
        //將當前的新結點定義為表尾終端結點
        r = p;
    }
    
    //將尾指針的next = null
    r->next = NULL;
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容