技能篇-數(shù)據(jù)結(jié)構(gòu)和算法篇-基礎(chǔ)算法與結(jié)構(gòu)( 一 )

TZ:我們恐懼的往往不是黑暗,而是光明

一 : 科普一分鐘

什么是數(shù)據(jù)結(jié)構(gòu)和算法,二者有和聯(lián)系呢.
其實一種是數(shù)據(jù)存儲的方式,一種是一種實現(xiàn)功能的手段.
我最近經(jīng)常做飯,打個比方,就好比做菜一樣,我們所用的食材就是數(shù)據(jù)結(jié)構(gòu),我們做同樣的菜,食材有新鮮的有不新鮮的,肉質(zhì)有老的有嫩的,同樣算法好比廚師的廚藝,有高手和菜鳥,兩者的配合會產(chǎn)生各種奇妙的結(jié)果,所以算法和數(shù)據(jù)結(jié)構(gòu) 是程序的重要組成部分.

二 : 數(shù)據(jù)結(jié)構(gòu)簡述和舉例

1. 概述:何為數(shù)據(jù)結(jié)構(gòu)?

數(shù)據(jù)結(jié)構(gòu)其實就是計算機存儲,組織數(shù)據(jù)的方式.
相互之間存在一種或者多種特定關(guān)系的數(shù)據(jù)元素的集合

2. 常見的數(shù)據(jù)結(jié)構(gòu)有哪些?

大家都學(xué)過數(shù)據(jù)結(jié)構(gòu)這門課程,接下來我們來復(fù)習(xí)一下,我們都學(xué)過哪些數(shù)據(jù)結(jié)構(gòu).
(1) 線性表
包括我們常用的數(shù)組,鏈表,棧,和隊列.
(2)樹
(3)圖

3.常見的邏輯結(jié)構(gòu)

什么是邏輯結(jié)構(gòu)?個人理解就是有點"紙上談兵"的意思,類似于草圖或者說是思路結(jié)構(gòu)
(1)集合結(jié)構(gòu) : 把雜亂的數(shù)據(jù)放進一個大鍋里.

集合結(jié)構(gòu).jpg

(2)線性結(jié)構(gòu) : 內(nèi)存是連續(xù)的,類似于"鐵索連環(huán)"

線性結(jié)構(gòu) .jpg

(3)樹形結(jié)構(gòu) : 這個結(jié)構(gòu)的特點就是....暫時理解像一顆樹就好了

樹形結(jié)構(gòu) .jpg

(4)圖形結(jié)構(gòu) : 這個結(jié)構(gòu)特點內(nèi)存可以是連續(xù)的 也可以是不連續(xù)的

圖形結(jié)構(gòu)

4.存儲結(jié)構(gòu)
說完了邏輯結(jié)構(gòu),我們說說存儲結(jié)構(gòu),
(1) 順序存儲結(jié)構(gòu)

順序存儲.jpg

(2)鏈?zhǔn)酱鎯Y(jié)構(gòu)

鏈?zhǔn)酱鎯Y(jié)構(gòu).jpg
  1. 隊列

假如此時我們做一個解密算法
把一個數(shù)據(jù)串 第一個數(shù)扔掉,第二個數(shù)放到尾部,第三個數(shù)扔掉,第四個數(shù)放到尾部.....等等, 刪掉的數(shù)按照移除順序就是我們要的結(jié)果 如何實現(xiàn)呢?

這其實就是隊列 ,隊列是先進先出的,就好像我們排隊買票,先站排的人最先買到票,最先離開,好像兩頭開口的一個通道.

我們用head 來標(biāo)注 隊列頭 用tail 來標(biāo)注隊列尾部
注意 這里說的隊尾 是值 尾部后+ 1 的位置

#include <stdio.h>
int main()
 {
    int q[102] = {0,6,3,1,7,5,8,2,9,4},head,tail;
  //初始化隊列
head = 1;
tail = 10;

while(head < tail){

//打印隊首
printf("%d",q[head]);

//出隊
head ++

//將新隊首數(shù)添加到隊尾

q[tail] = q[head];
tail++;
//再次隊首移除出隊
head++
}

return 0;
 }

6.棧
簡單的說一下棧,我們吃的薯片或者彈夾,都類似棧,先進后出.
現(xiàn)在我們做個簡單的小例子簡單分析一下
做一個判斷一個串 是否是回文
什么是回文, AABB ,ABBA ,ABCBA這種對稱的叫回文

我們用一個指向棧頂?shù)淖兞縯op

#include<stdio.h>
#include<string.h>
char a[101],s[101];
int i,len,mid,next,top;
gets(a)//讀入一個串
len = strlen(a);
mid = len/2 - 1;

top = 0;
//mid前面數(shù)一次入棧
for(i = 0; i <= mid ;i++){
s[++top] = a[i];

}
//判斷奇偶性
if(len%2 == 0)
  next = mid + 1;
else
 next = mid + 2;

//匹配
for(i = mext ; i<len-1;i++){
if(a[i] != s[stop])
breal;
top -- ;
}


if(top == 0){
printf("是回文");

}else{

printf("不是回文");
}
return 0;
  1. 鏈表
鏈表.jpg

每一個鏈表的節(jié)點有兩個部分組成,左邊的部分存放的是具體的數(shù)值,右邊存放的是下一個節(jié)點的地址.我們可以用一個結(jié)構(gòu)體來描述它

struct node{
int data;
struct node *next;
}

現(xiàn)在實現(xiàn) 插入某個數(shù)到序列里,簡單來說就是拆鏈和補鏈的過程.

#include <sdio.h>
#include <stdlib.h>
struct node{
int data;
struct node *next;
};

int main(){
 struct node *head,*p,*q,*t;
int i,n,a;
scanf("%d",&n);
head = NULL;//初始化
for(i = 1;i <= n;i++)
{
scanf("%d",&a);
//動態(tài)申請空間
p = (stuct node*)malloc(sizeof(stuct node));
p->data = a;//存放到當(dāng)前節(jié)點
p ->next = NULL;
if(head ==NULL){
  head = p;//創(chuàng)建第一個節(jié)點
}else{
    q->next= p;
}
q = p;


//輸入插入的數(shù)
scanf("%d",&a);
t = head;
while(t != NULL){
if(t->next ==NULL || 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;
}

return 0;
}

對于數(shù)據(jù)結(jié)構(gòu)和其存儲代碼只是簡單描述,日后慢慢講解.

三 : 算法簡述和常用基礎(chǔ)算法-排序

在工作中和學(xué)習(xí)中我們經(jīng)常聽到算法算法的,在上學(xué)的時候我們通常會學(xué)習(xí)算法這門單獨學(xué)科的課程,講的主要是,通過計算機語言編程解決一些和應(yīng)用題一樣的一些問題.

好,算法可以理解為是一種手段,或者一種解決方式,一系列的計算步驟,用來將輸入數(shù)據(jù)轉(zhuǎn)化為輸出結(jié)果

1.算法的意義

(1)用于解決特定的問題
(2)解決同一個問題的不同算法的效率常常相差很大,這種差距的影響往往比硬件和軟件方便的差距還要大.

2. 常用算法

(1)排序
(2)加密

3. 算法的優(yōu)劣

怎樣科學(xué)的評判一個算法的優(yōu)劣,算法那么重要,不同的算法會很大程度上影響運行效率
時間復(fù)雜度 : 估算程序指令的執(zhí)行次數(shù)
空間復(fù)雜度 估算所需占用的存儲空間

這兩個的計算方式我們以后的博客仔細講解

4. 算法的優(yōu)化方向

(1)用盡量少的內(nèi)存空間
(2)用盡量少的執(zhí)行步驟
(3)空間換時間
(4)時間換空間

5. 排序算法

(1)桶排序

1965年有兩個大神, E.J IssacR.C Singleton 提出來的基本思想.
下面簡單總結(jié)一下這種算法的思想

#include <stdio.h>
int main()
{
int a[10] , i,j,t;
for (i = 0 ; i <= 10 ; i++)
{
//初始化一下數(shù)組
a[i] = 0;
};

//循環(huán)輸入7個數(shù) 對應(yīng)的數(shù)字放到對應(yīng)的桶中
for (int i = 0 ; i <7; i++ )
{
scanf("%d",&t);
a[t]++;
};

//輸出打印
for(i = 0; i<= 10;i++){
  for(j = 0;j <= a[i]; j++)
    printf("%d",i);//每個桶中的數(shù)出現(xiàn)幾次就打印幾次
};

return 0;
}

總結(jié)分析 : 這是一個簡化版的桶排序實際上桶排序要復(fù)雜很多這里就是和大家分析一下思路,下面畫一個圖來捋順一下

桶排序.jpg

每個桶的號碼就是存放自己的專屬數(shù)字,比如說1號桶專門存放的就是數(shù)字 1 5號桶專門存放 數(shù)組 5 初見幾次 就往對應(yīng)的桶里面 增加一次,也就是代碼中的 a[t]++;

可以看的出來這種算法需要的空間很大,也就是說,我們要準(zhǔn)備很多的 這點并不可取

接下來看看其時間復(fù)雜度O(M+N) (計算方式以后博客會說明)

(2)冒泡排序

1956 年就有人曾經(jīng)研究過冒泡排序,之后很多人研究改進過,但結(jié)果都令人失望,正如 高德納 所說 冒泡排序除了它迷人的名字和導(dǎo)致了某些有趣的理論問題這一事實之外,似乎沒什么值得推薦的

#include <stdio.h>
int main(){

int a[100],i,j,t,n;
//輸入n 個數(shù)存放到數(shù)組 a中
for(i = 0 ; i <= n ; i++){
   scanf("%d",&a[i]);
};

//冒泡排序交換核心
for(i = 0 ; j <= n-1;i++){
   for(j = 1 ; j < n - i;j++)
     {
         if( a[j] < a[j+1] ){
               t = a[j]; a[j] = a[j+1]; a[j+1] = t;
            }
     }
}

//輸出結(jié)果 
for(i = 0 ;i <= n;i++){

printf("%d".a[i]);
}

return 0;
}

總結(jié) : 冒泡排序就是兩兩交互比較大小,核心代碼中的外層循環(huán) 是 走的 數(shù) 控制交換了幾趟,每一趟都能決定出一個最大的數(shù) 或者 最小的數(shù) ,為什么是 n-1 因為最后一趟不用跑也確定了,因為就剩下那一個數(shù)了,同理內(nèi)層循環(huán) n - i 因為每趟交換 確定了一個數(shù) ,所以我們內(nèi)層循環(huán) 的時候 就要 排除那個已經(jīng) 確定的 數(shù).

接下來看看 冒泡排序時間復(fù)雜度 ->O(N2)

(3)快速排序

1960年 東尼霍爾 提出了快速排序 ,之后又有人做了優(yōu)化.

思路 : 簡單排序就是確定一個數(shù) 基數(shù)基數(shù)左邊的數(shù)小于基數(shù)(這里說的是遞增),讓基數(shù)右邊的數(shù)大于基數(shù),
然后再去基數(shù)左邊做同樣操作 ,基數(shù)右邊做同樣操作

接下來上圖來分析一下

快速排序.jpg

圖中 (1) 我們通常把首個數(shù)設(shè)置為基數(shù) 我們在序列左邊放了一個檢索 i 在 序列 右邊放了一個檢索 j 進行檢索

當(dāng)i 發(fā)現(xiàn)了 比 '6' 大的數(shù)則停下,當(dāng) j 發(fā)現(xiàn)了 比 6 小的數(shù)則停下,然后 把發(fā)現(xiàn)的數(shù)交換位置 如圖(2)

交換完畢 i 和 j 繼續(xù)移動 如圖(3) 繼續(xù)交換 最后 i 和 j 相遇 檢索完畢

最后把 3 和 6 交換位置,以 6為基數(shù)的 檢索結(jié)束 ,接下來 6 左邊的數(shù) 和 6 右邊的數(shù)進行 同理操作

#include <stdio.h>

int a[101],n;

//我們把排序封裝成方法 參數(shù) 分別是 左邊啟點 和 右邊啟點
void quicksort (int left,int right){
int i,j,t,temp;

if(left > right)
return;
};


temp = a[left];//確定基數(shù)
i = left;
j = right;
while(i != j){
//右邊先開始檢索
while( a[j] >= temp && i < j)
     j--;
}

//從左往右檢索
while( a[i] <= temp && i < j ){
  i++
}

//交換兩個數(shù)的位置
if( i < j) //沒有相遇
{
  t = a[i];
 a[i] = a[j];
a[j] = t;
}

}

//基數(shù)歸位

a[left] = a[i];
a[i] = temp;

quicksort(left,i-1);//處理基數(shù)左邊的
quicksort(i+1,right);//處理基數(shù)右邊的

return;
}

  int main()
 {
 int i,j;
//讀取數(shù)據(jù)
scanf("%d",&n);
for(i = 1;i < n;i++)
 scanf("%d",&a[1]);
}

//調(diào)用
quicksort(1,n);
//輸出
for(i = 1; i <= n ;i++){
   printf("%d", a[i]);
}

return 0;

總結(jié) : 為什么 j 要先移動,而 i 要后移動呢

因我我們把基數(shù)設(shè)置在了左邊,我們假設(shè)一種特殊情況,就是數(shù)據(jù)序列已經(jīng)是從小到大拍好了的,假如我們從左邊開始檢索,因為基數(shù)在左邊,循環(huán)條件可訂貨滿足,所以,i 肯定移動到下一個,所以就發(fā)生了錯誤,所以我們要右j邊先移動.

四 : 總結(jié)

簡單的聊過了數(shù)據(jù)結(jié)構(gòu)和算法,一個好的程序,我們不僅要考慮它的運行速度,也要配上好的數(shù)據(jù)結(jié)構(gòu),也就是數(shù)據(jù)的存儲方式,數(shù)據(jù)結(jié)構(gòu)往往配合著算法的使用 更佳炫酷.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • 概述 排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    蟻前閱讀 5,215評論 0 52
  • 概述:排序有內(nèi)部排序和外部排序,內(nèi)部排序是數(shù)據(jù)記錄在內(nèi)存中進行排序,而外部排序是因排序的數(shù)據(jù)很大,一次不能容納全部...
    每天刷兩次牙閱讀 3,742評論 0 15
  • 查找和排序都是程序設(shè)計中經(jīng)常用到的算法。查找相對而言較為簡單,不外乎順序查找、二分查找、哈希表查找和二叉排序樹查找...
    eagleRock閱讀 5,612評論 0 14
  • 9.3.3 快速排序 ??快速排序?qū)⒃瓟?shù)組劃分為兩個子數(shù)組,第一個子數(shù)組中元素小于等于某個邊界值,第二個子數(shù)組中的...
    RichardJieChen閱讀 1,860評論 0 3
  • 2017.5.19 1、早上戶外活動、活動中我應(yīng)該屬于他燃型。 活動要求是集齊大家身上所有物品進行連接、那組最長就...
    Iris_1987閱讀 187評論 0 0