數據結構與算法 學習筆記

1,判斷鏈表是否存在環型鏈表

問題:判斷一個鏈表是否存在環,例如下面這個鏈表就存在一個環:

 

例如N1-N2-N3-N4-N5-N2就是一個有環的鏈表,環的開始結點是N5

這裏有一個比較簡單的解法。設置兩個指針p1,p2。每次循環p1向前走一步,p2向前走兩步。直到p2碰到NULL指針或者兩個指針相等結束循環。如果兩個指針相等則說明存在環。

 

struct link

{

    int data;

    link next;

};

bool IsLoop(link head)

{

    link p1=head, p2 = head;

     if (head ==NULL  head-next ==NULL)

     {

          return false;

     }

    do{

        p1= p1-next;

        p2 = p2-next-next;

    } while(p2 && p2-next && p1!=p2);    

     if(p1 == p2)

          return true;

     else

          return false;

}

 

2,鏈表反轉

5, 找出單向鏈表的中間結點

這道題和解 判斷鏈表是否存在環 ,我用的是非常類似的方法,只不過結束循環的條件和函數返回值不一樣罷了。設置兩個指針p1,p2。每次循環p1向前走一步,p2向前走兩步。當p2到達鏈表的末尾時,p1指向的時鏈表的中間。

link mid(link head)

{

       link p1,p2;

       p1=p2=head;

       if(head==NULL  head-next==NULL)

              return head;

       do {

              p1=p1-next;

              p2=p2-next-next;

       } while(p2 && p2-next);

       return p1;

}

6, 按單詞反轉字符串

並不是簡單的字符串反轉,而是按給定字符串裏的單詞將字符串倒轉過來,就是說字符串裏面的單詞還是保持原來的順序,這裏的每個單詞用空格分開。例如

Here is www.fishksy.com.cn

經過反轉後變爲

www.fishksy.com.cn is Here

如果只是簡單的將所有字符串翻轉的話,可以遍歷字符串,將第一個字符和最後一個交換,第二個和倒數第二個交換,依次循環。其實按照單詞反轉的話可以在第一遍遍歷的基礎上,再遍歷一遍字符串,對每一個單詞再反轉一次。這樣每個單詞又恢復了原來的順序。

char reverse_word(const char str)

{

     int len = strlen(str);

     char restr = new char[len+1];

     strcpy(restr,str);

     int i,j;

     for(i=0,j=len-1;ij;i++,j--)

     {

          char temp=restr[i];

          restr[i]=restr[j];

          restr[j]=temp;

     }

     int k=0;

     while(klen)

     {

          i=j=k;

          while(restr[j]!=' ' && restr[j]!='0' )

               j++;

          k=j+1;

          j--;

          for(;ij;i++,j--)

          {

               char temp=restr[i];

               restr[i]=restr[j];

               restr[j]=temp;

          }

     }

     return restr;

}

 

單向鏈表的反轉是一個經常被問到的一個面試題,也是一個非常基礎的問題。比如一個鏈表是這樣的: 1-2-3-4-5 通過反轉後成爲5-4-3-2-1。最容易想到的方法遍歷一遍鏈表,利用一個輔助指針,存儲遍歷過程中當前指針指向的下一個元素,然後將當前節點元素的指針反轉後,利用已經存儲的指針往後面繼續遍歷。源代碼如下:

struct linka {

     int data;

     linka next;

};

 

void reverse(linka& head)

{

     if(head ==NULL)

          return;

     linkapre, cur, ne;

     pre=head;

     cur=head-next;

     while(cur)

     {

          ne = cur-next;

          cur-next = pre;

          pre = cur;

          cur = ne;

     }

     head-next = NULL;

     head = pre;

}

3, 判斷兩個數組中是否存在相同的數字

後來發現有一個 O(n)算法。因爲兩個數組都是排好序的。所以只要一次遍歷就行了。首先設兩個下標,分別初始化爲兩個數組的起始地址,依次向前推進。推進的規則是比較兩個 數組中的數字,小的那個數組的下標向前推進一步,直到任何一個數組的下標到達數組末尾時,如果這時還沒碰到相同的數字,說明數組中沒有相同的數字。

bool findcommon2(int a[], int size1, int b[], int size2)

{

     int i=0,j=0;

     while(i<size1 && j<size2)

     {

          if(a[i]==b[j])

               return true;

          if(a[i]>b[j])

               j++;

          if(a[i]<b[j])

               i++;

     }

     return false;

}

4, 最大子序列

問題:

給定一整數序列A1, A2,... An (可能有負數),求A1~An的一個子序列Ai~Aj,使得Ai到Aj的和最大

 

在給出線性算法之前,先來看一個對窮舉算法進行優化的算法,它的算法複雜度爲O(n^2)。其實這個算法只是對對窮舉算法稍微做了一些修改:其實子序列的和我們並不需要每次都重新計算一遍。假設Sum(i, j)是A[i] ... A[j]的和,那麼Sum(i, j+1) = Sum(i, j) + A[j+1]。利用這一個遞推,我們就可以得到下面這個算法:

int max_sub(int a[],int size)

{

     int i,j,v,max=a[0];

     for(i=0;i<size;i++)

     {

          v=0;

          for(j=i;j<size;j++)

          {

               v=v+a[j];Sum(i, j+1) = Sum(i, j) + a[j+1]

               if(v>max)

                    max=v;

          }

     }

     return max;

}

最優:

int max_sub2(int a[], int size)

{

     int i,max=0,temp_sum=0;

     for(i=0;i<size;i++)

     {

          temp_sum+=a[i];

          if(temp_sum>max)

               max=temp_sum;

          else if(temp_sum<=0)

               temp_sum=0;

     }

     return max;

}

在這一遍掃描數組當中從左到右記錄當前子序列的和temp_sum若這個和不斷增加那麼最大子序列的和max也不斷增加(不斷更新max)。如果往前掃描中遇到負數,那麼當前子序列的和將會減小。此時temp_sum 將會小於max,當然max也就不更新。如果temp_sum降到0時,說明前面已經掃描的那一段就可以拋棄了,這時將temp_sum置爲0。然後,temp_sum將從後面開始將這個子段進行分析,若有比當前max大的子段,繼續更新max。這樣一趟掃描結果也就出來了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章