數據結構 數據表 學習筆記

數據結構學習筆記
線性表
概念
全名爲線性存儲結構。使用線性表存儲數據的方式可以這樣理解,即“把所有數據用一根線兒串起來,再存儲到物理空間中”
分類
將數據依次存儲在連續的整塊物理空間中,這種存儲結構稱爲順序存儲結構(簡稱順序表)
數據分散的存儲在物理空間中,通過一根線保存着它們之間的邏輯關係,這種存儲結構稱爲鏈式存儲結構(簡稱鏈表)
順序表
概念
順序表對數據的物理存儲結構也有要求。順序表存儲數據時,會提前申請一整塊足夠大小的物理空間,然後將數據依次存儲起來,存儲時做到數據元素之間不留一絲縫隙。如圖
在這裏插入圖片描述
Tips:順序表在申請時需要爲順序表記錄兩個數據。
1:順序表申請的存儲容量
2:順序表的長度,也就是表中存儲數據元素的個數
3:順序表申請的存儲容量要大於順序表的長度
聲明順序表

typedef struct Table{
    int* head;//聲明瞭一個名爲head的長度不確定的數組,也叫“動態數組”
    int length;//記錄當前順序表的長度
    int size;	//記錄順序表分配的存儲容量
}table;

Tips:
head 是我們聲明的一個未初始化的動態數組,不要只把它看做是普通的指針
初始化順序表
給鏈表頭 head 動態數據申請足夠大小的物理空間;
給 size 和 length 賦初值;

table initTable(){
table t;
//構造一個空的順序表,動態申請存儲空間
    t.head=(int*)malloc(Size*sizeof(int)); 
    if (!t.head) //如果申請失敗,作出提示並直接退出程序
    {
        printf("初始化失敗");
        exit(0);
    }
    t.length=0;//空表的長度初始化爲0
    t.size=Size;//空表的初始存儲空間爲Size
    return t;
}

順序表元素賦值

for (int i=1; i<=Size; i++) 
{
    t.head[i-1]=i;
    t.length++;
}

順序表顯示數據
//輸出順序表中元素的函數

void displayTable(table t)
{
    for (int i=0;i<t.length;i++)
    {
        printf("%d ",t.head[i]);
    }
    printf("\n");
}

順序表插入元素
插入函數,其中,elem爲插入的元素,add爲插入到順序表的位置

table addTable(table t,int elem,int add)
{
//判斷插入本身是否存在問題(如果插入元素位置比整張表的長度+1還大(如果相等,是尾隨的情況),或者插入的位置本身不存在,程序作爲提示並自動退出)
if (add>t.length+1||add<1)
{
printf("插入位置有問題");
    return t;
 }
//做插入操作時,首先需要看順序表是否有多餘的存儲空間提供給插入的元素,如果沒有,需要申請
if (t.length==t.size)
{
    t.head=(int *)realloc(t.head, (t.size+1)*sizeof(int));
    if (!t.head)
    {
       printf("存儲分配失敗");
       return t;
    }
    t.size+=1;
} 
//插入操作,需要將從插入位置開始的後續元素,從最後面一個元素逐個後移。
for (int i=t.length-1; i>=add-1; i--)
{
    t.head[i+1]=t.head[i];
}
//後移完成後,直接將所需插入元素,添加到順序表的相應位置
t.head[add-1]=elem;
//由於添加了元素,所以長度+1
t.length++;
return t;
}

順序表刪除元素

table delTable(table t,int add)
{
if (add>t.length || add<1) 
{
        printf("被刪除元素的位置有誤");
        exit(0);
    }
    //刪除操作
for (int i=add; i<t.length; i++) 
{
    //找到需要刪除的元素,使其後面的元素前移一位
        t.head[i-1]=t.head[i]; 
    }
    t.length--;
    return t;
}

順序表查找元素
//查找函數,其中,elem表示要查找的數據元素的值

int selectTable(table t,int elem)
{
for (int i=0; i<t.length; i++) 
{
        if (t.head[i]==elem) 
{
            return i+1;
        }
}
//如果查找失敗,返回-1
    return -1;
}

順序表修改元素
//更改函數,其中,elem爲要更改的元素,newElem爲新的數據元素

table amendTable(table t,int elem,int newElem)
{
//先查找到該元素位置下標
int add=selectTable(t, elem); 
//由於返回的是元素在順序表中的位置,所以-1就是該元素在數組中的下標
t.head[add-1]=newElem;
    return t;
}

鏈表
概念
鏈表,別名鏈式存儲結構或單鏈表,用於存儲邏輯關係爲 “一對一” 的數據。與順序表不同,鏈表不限制數據的物理存儲狀態,換句話說,使用鏈表存儲的數據元素,其物理存儲位置是隨機的。
在這裏插入圖片描述
Tips:鏈表中每個數據的存儲都由以下兩部分組成:
數據元素本身,其所在的區域稱爲數據域;
指向直接後繼元素的指針,所在的區域稱爲指針域
在這裏插入圖片描述
一個完整的鏈表需要由以下幾部分構成:
在這裏插入圖片描述
頭指針:一個普通的指針,它的特點是永遠指向鏈表第一個節點的位置。很明顯,頭指針用於指明鏈表的位置,便於後期找到鏈表並使用表中的數據;

節點:鏈表中的節點又細分爲頭節點、首元節點和其他節點:
頭節點:其實就是一個不存任何數據的空節點,通常作爲鏈表的第一個節點。對於鏈表來說,頭節點不是必須的,它的作用只是爲了方便解決某些實際問題;
首元節點:由於頭節點(也就是空節點)的緣故,鏈表中稱第一個存有數據的節點爲首元節點。首元節點只是對鏈表中第一個存有數據節點的一個稱謂,沒有實際意義;
其他節點:鏈表中其他的節點
注意:鏈表中有頭節點時,頭指針指向頭節點;反之,若鏈表中沒有頭節點,則頭指針指向首元節點。
聲明鏈表
typedef struct Link
{
char elem; //代表數據域
struct Link * next; //代表指針域,指向直接後繼元素
}link;
//link爲節點名,每個節點都是一個 link 結構體
//由於指針域中的指針要指向的也是一個節點,因此要聲明爲 Link 類型

創建鏈表

link * initLink()
{
    //創建一個頭結點
link * p=(link*)malloc(sizeof(link)); 
//聲明一個指針指向頭結點,
link * temp=p; 
    //生成鏈表
for (int i=1; i<5; i++)
{
        link *a=(link*)malloc(sizeof(link));
        a->elem=i;
        a->next=NULL;
//此處temp->next其實就是head->next,temp->next此時指向的是a的地址。就是head->next指向a的地址
        temp->next=a;
//將a的地址值賦值給temp,即temp指向了節點a。
        temp=temp->next; 
    }
    return p;
}

帶頭節點的鏈表

link * initLink()
{
    //創建一個頭結點
link * p=(link*)malloc(sizeof(link)); 
//聲明一個指針指向頭結點,
link * temp=p; 
    //生成鏈表
for (int i=1; i<5; i++)
{
        link *a=(link*)malloc(sizeof(link));
        a->elem=i;
        a->next=NULL;
//此處temp->next其實就是head->next,temp->next此時指向的是a的地址。就是head->next指向a的地址
        temp->next=a;
//將a的地址值賦值給temp,即temp指向了節點a。
        temp=temp->next; 
    }
    return p;
}

不帶頭結點的鏈表(帶首元節點)

link * initLink()
{
    link * p=NULL;         //創建頭指針
    link * temp = (link*)malloc(sizeof(link));//創建首元節點    
    temp->elem = 1;       //首元節點先初始化
    temp->next = NULL;
    p = temp;             //頭指針指向首元節點
    //從第二個節點開始創建
for (int i=2; i<5; i++)
{
        //創建一個新節點並初始化
        link *a=(link*)malloc(sizeof(link));
        a->elem=i;
        a->next=NULL;
        //將temp節點與新建立的a節點建立邏輯關係
        temp->next=a;
        //指針temp每次都指向新鏈表的最後一個節點,
//其實就是 a節點,這裏寫temp=a也對
        temp=temp->next;
    }
    //返回建立的節點,只返回頭指針 p即可,通過頭指針即可找到整個鏈表
    return p;
}

鏈表插入數據
在這裏插入圖片描述
插入思想:
雖然新元素的插入位置不固定,但是鏈表插入元素的思想是固定的,只需做以下兩步操作,即可將新元素插入到指定的位置:
將新結點的 next 指針指向插入位置後的結點;
將插入位置前結點的 next 指針指向插入結點;
//p爲原鏈表,elem表示新數據元素,add表示新元素要插入的位置

link * insertElem(link * p,int elem,int add)
{
    link * temp=p;//創建臨時結點temp
for (int i=1; i<add; i++) //首先找到要插入位置的上一個結點
{
        if (temp==NULL) {
            printf("插入位置無效\n");
            return p;
        }
        temp=temp->next;
    }   
    //創建插入結點c
    link * c=(link*)malloc(sizeof(link));
    c->elem=elem;    
    c->next=temp->next; //向鏈表中插入結點
    temp->next=c;
    return  p;
}

鏈表刪除數據
//p爲原鏈表,add爲要刪除元素的值

link * delElem(link * p,int add)
{
    link * temp=p;
    //temp指向被刪除結點的上一個結點
for (int i=1; i<add; i++) 
{
        temp=temp->next;
}
//單獨設置一個指針指向被刪除結點,以防丟失
    link * del=temp->next; 
//刪除某個結點的方法就是更改前一個結點的指針域    
temp->next=temp->next->next; 
    free(del);//手動釋放該結點,防止內存泄漏
    return p;
}

鏈表查找數據
//p爲原鏈表,elem表示被查找元素、

int selectElem(link * p,int elem)
{
//新建一個指針t,初始化爲頭指針 p
    link * t=p;
    int i=1;
    //由於頭節點的存在,因此while中的判斷爲t->next
    while (t->next)
    {
        t=t->next;
        if (t->elem==elem) 
        {
             return i;
         }
        i++;
    }
    //程序執行至此處,表示查找失敗
    return -1;
}

鏈表更新數據
//更新函數,其中,add 表示更改結點在鏈表中的位置,newElem 爲新的數據域的值

link *amendElem(link * p,int add,int newElem)
{
link * temp=p;
//在遍歷之前,temp指向首元結點
    temp=temp->next; 
    //遍歷到被刪除結點
for (int i=1; i<add; i++) 
{
        temp=temp->next;
    }
    temp->elem=newElem;
    return p;
}

計算鏈表的大小

size_t LinkListSize(LP_RECSEGMENT_INFO_V20 *head)
{
	if(head==NULL)
	{
		return 0;
	}
	LP_RECSEGMENT_INFO_V20 *cur=head;  
	size_t size=0;
	for(;*cur!=NULL;*cur=(*cur)->pNext)
	{
		size++;
	}
	if(*cur==NULL)
	{
		return 0;
	}
	return size;
}
struct Node
{
    int data ;
    Node *next ;
};
typedef struct Node Node ;

(1)已知鏈表的頭結點head,寫一個函數把這個鏈表逆序 ( Intel)

Node * ReverseList(Node *head) //鏈表逆序
{
    if ( head == NULL || head->next == NULL )
    return head;
    Node *p1 = head ;
    Node *p2 = p1->next ;
    Node *p3 = p2->next ;
    p1->next = NULL ;
    while ( p3 != NULL )
    {
        p2->next = p1 ;
        p1 = p2 ;
        p2 = p3 ;
        p3 = p3->next ;
    }
    p2->next = p1 ;
    head = p2 ;
    return head ;
}

(2)已知兩個鏈表head1 和head2 各自有序,請把它們合併成一個鏈表依然有序。(保留所有結點,即便大小相同)

Node * Merge(Node *head1 , Node *head2)
{
    if ( head1 == NULL)
        return head2 ;
    if ( head2 == NULL)
        return head1 ;
    Node *head = NULL ;
    Node *p1 = NULL;
    Node *p2 = NULL;
    if ( head1->data < head2->data )
    {
        head = head1 ;
        p1 = head1->next;
        p2 = head2 ;
    }
    else
    {
        head = head2 ;
        p2 = head2->next ;
        p1 = head1 ;
    }
    Node *pcurrent = head ;
    while ( p1 != NULL && p2 != NULL)
    {
    if ( p1->data <= p2->data )
    {
        pcurrent->next = p1 ;
        pcurrent = p1 ;
        p1 = p1->next ;
    }
    else
    {
        pcurrent->next = p2 ;
        pcurrent = p2 ;
        p2 = p2->next ;
    }
}
if ( p1 != NULL )
    pcurrent->next = p1 ;
if ( p2 != NULL )
    pcurrent->next = p2 ;
return head ;
}

(3)已知兩個鏈表head1 和head2 各自有序,請把它們合併成一個鏈表依然有序,這次要求用遞歸方法進行。(Autodesk)

Node * MergeRecursive(Node *head1 , Node *head2)
{
if ( head1 == NULL )
    return head2 ;
if ( head2 == NULL)
    return head1 ;
Node *head = NULL ;
if ( head1->data < head2->data )
{
    head = head1 ;
    head->next = MergeRecursive(head1->next,head2);
}
else
{
    head = head2 ;
    head->next = MergeRecursive(head1,head2->next);
}
return head ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章