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。這樣一趟掃描結果也就出來了。