常見演算法及問題需注意的技巧與簡單實現
1、合併兩個有序陣列A,B,並使合併後的陣列在A中。(假設A中有足夠的空間儲存兩個陣列中的元素)
void mergeSortedArray(int A[],int m, int B[],int n)
{
int index = n + m;//從後向前貼上可以避免沒有判斷的元素被覆蓋
while(m>0 && n>0){
if(A[m-1] > B[n-1]){
A[--index] = A[--m];
}
else{
A[--index] = B[--n];
}
}
while(n>0){
A[--index] = B[--n];
}
while(m>0){
A[--index] = A[--m];
}
}
1-1 寫一個函式 void *memmove(void *dest, const void *src, size_t n) 將一個字串陣列移動到目的地址為起點的地址。
void *memmove(void *dest, const void *src, size_t n)
{
char *p1 = dest;
const char *p2 = src;
//要考慮兩個地址是否會有記憶體重疊的部分,不考慮會造成未移動的資料被覆蓋而丟失
if(p2 < p1)//若源地址比目的地址小;從源地址的最後逆序賦值可以避免未拷貝的資料被覆蓋
{
p2 += n;
p1 += n;
while(n-- != 0)
*--p1 = *--p2;
}
else{
while(n-- != 0)
*p1++ = *p2++;
}
return p1;
}
v = *p++; // * 和 ++ 同級,自右向左結合 等價於 v = *(p++)
// 先取p所指目標變數的值,賦予變數v; 再對 p 對進行增1,即指標p指向下一個目標變數。
v = (*p)++; // 先將變數 *p 的值賦予v,再對變數 *p進行增1
2、連結串列要刪除當前所指的節點,而不是當前指標的下一個節點:
技巧是:把當前節點的值用next的值覆蓋,然後刪除next節點,
相當於刪除當前節點,實際是刪除了next節點而將next的值拿來覆蓋了當前節點的值。
3、埃拉托色尼(The Sieve of Eratosthenes) 質數篩選法(常考點)
演算法的核心思想:先將範圍內的數都假設為質數,然後從最小的質數開始,將質數的倍數更改為非質數。
//假設求2-200之間的質數
#include<stdio.h>
#include<string.h>
#define n 200
int main()
{
int flag[n+1];
memset(flag,1,sizeof(flag));//定義在string.h中
flag[0] = flag[1] = 0;
for(int i=2; i<n; ++i)
{
if(flag[i]){
for(int j=2; i*j<n; ++j){//將質數的倍數都置為0
flag[i*j] = 0;
}
}
}
int cnt = 0;//記錄質數個數
for(int i=2; i<n; ++i){
if(flag[i]){
++cnt;
printf("%d ",i);
}
}
printf("\n 質數個數為:\n",cnt);
return 0;
}
演算法的雙層迴圈處還可以優化以提高執行效率
//假設求2-200之間的質數
#include<stdio.h>
#include<string.h>
#define n 200
int main()
{
int flag[n+1];
memset(flag,1,sizeof(flag));//定義在string.h中
flag[0] = flag[1] = 0;
for(int i=2; i<sqrt(n); ++i)//外層迴圈結束條件到sqrt(n)就可以 判斷條件也可以寫為 i*i<n
//一個合數n必有一個不大於sqrt(n)的正因子,故一個數若是沒有小於sqrt(n)的正因子,則說明它是一個素數
{
if(flag[i]){
for(int j=i*i; j<n; j+=i){//將質數的倍數都置為0。內層迴圈從i*i開始即可,之前的已經判斷過
flag[j] = 0;
}
}
}
int cnt = 0;//記錄質數個數
for(int i=2; i<n; ++i){
if(flag[i]){
++cnt;
printf("%d ",i);
}
}
printf("\n 質數個數為:\n",cnt);
return 0;
}
4、輸入一行字串,統計其中包括多少單詞,單詞之間用空格分隔。
#include<stdio.h>
#include<string.h>
main()
{
char str[200];//定義字元陣列,儲存字串
int i;
int space=0;//空格標誌,0 表示新空格,1 表示連續空格
int num=0;//單詞數量
printf("請輸入字串:");
gets(str);
if(str[0]==' ')//去掉第一行開頭的空格
space=1;
for(i=0;str[i]!='\0';i++)// 這個迴圈很關鍵,裡邊的判斷很巧妙
{
if(str[i]==' ')//處理連續空格的情況,當前字元為空格
{
if(space==0)//新空格
{
space=1;//表示連續空格
num=num+1;
}
}
else
space=0;// 新空格
}
if(space==0)//如果字串不以空格結束,則單詞數增 1
num=num+1;
printf("單詞總數為: %d\n",num);//輸出結果
}
程式分析:本題容易出錯的地方主要是對字串前後空格的判斷。
5、反轉一個單連結串列(reverse linked list)
遞迴法
struct ListNode {
int val;
struct ListNode *next;
};
//遞迴反轉整個連結串列
struct ListNode* reverse(struct ListNode* head) {
if (head->next == null) return head;
struct ListNode* last = reverse(head->next);
head->next->next = head;//將下一個節點變成當前節點的前驅節點
head->next = null;
return last;
}
非遞迴法(迭代法)
typedef struct ListNode {
int val;
struct ListNode *next;
}ListNode;
ListNode reverseList(ListNode head)
{
ListNode pre = NULL;
while(head != NULL)
{
ListNode cur = head;
head = head->next;
cur->next = pre;
pre = cur;
}
return pre;
}
6、編寫一個函式,利用遞迴方法找出一個陣列中的最大值和最小值,要求遞迴呼叫函式的格式如下:
MinMaxValue(arr,n,&max,&min),其中arr是給定的陣列,n是陣列的個數,max、min分別是最大值和最小值。
void MinMaxValue(int arr[],int n, int* max, int* min)//這裡的man和min都是出參
{
if(n==1)
{
*max = arr[0];
*min = arr[0];
}
else
{
int tmp_max = arr[0];//因為arr+1操作,這裡在每次遞迴中都是變化的
int tmp_min = arr[0];
MinMaxValue(arr+1, n-1, max, min);
if(*max < tmp_max) *max = tmp_max;
if(*min > tmp_min) *min = tmp_min;
}
}
7、一個C語言程式的編譯到執行的過程
預處理:用於將所有的#include標頭檔案以及巨集定義替換成其真正的內容,預處理之後得到的仍然是文字檔案,但檔案體積會大很多。
編譯:這裡的編譯不是指程式從原始檔到二進位制程式的全部過程,而是指將經過預處理之後的程式轉換成特定彙編程式碼(assembly code)的過程。
彙編:將上一步的彙編程式碼轉換成機器碼(machine code),這一步產生的檔案叫做目標檔案,是二進位制格式。這一步會為每一個原始檔產生一個目標檔案。
連結:將多個目標文以及所需的**庫檔案(.so等)**連結成最終的可執行檔案(executable file)。
8、行內函數
呼叫行內函數時,編譯器首先檢查呼叫是否正確(型別安全檢查或者自動進行型別轉換)。
如果正確,則將行內函數的程式碼直接替換函式呼叫,並且用實參換形參,於是省去了函式呼叫的開銷。
因此,內聯機制增加了空間開銷而節約了時間開銷。(空間換時間)
行內函數與用 #define 命令實現的帶參巨集定義有些相似,但不完全相同:
用行內函數可以達到用 #define 巨集置換的目的,但不會出現 帶參巨集定義 的副作用: 如自增運算時,容易出現錯誤,因為巨集是直接替換引數的,比如:
#define square(a) (a)*(a)
int a = 1;
int re = square(a++);
// 可能 a = 3 re = 2
// 因為編譯器可能以不同的方式對錶達式((a++)*(a++)進行求值
慎用行內函數
1)使用行內函數可以節省執行時間,但卻增加了目標程式的長度
2)函式體內出現迴圈或遞迴等複雜的結構控制語句時,s不適合定義為行內函數
3)一個好的編譯器將會根據函式的函式體,自動取消不值得的內聯
相關文章
- iOS FTPManager的簡單使用及常見問題iOSFTP
- GarageSale Mac版常見問題及提示技巧Mac
- 常見加密演算法及常見加密演算法簡述加密演算法
- 棧的模擬實現及常見演算法演算法
- 【多執行緒】常見問題簡單總結執行緒
- Java常見排序演算法之插入排序-簡單的效能優化技巧Java排序演算法優化
- 淺談常見的七種加密演算法及實現加密演算法
- Git常見問題及解決Git
- tcp 實現簡單http 問題TCPHTTP
- Kafka常見的問題及解決方案Kafka
- Android Studio 中 Preview 常見問題和技巧AndroidView
- 專案答辯常見問題和技巧
- 常見排序演算法原理及JS程式碼實現排序演算法JS
- [python] asyncio庫常見問題與實踐案例Python
- 簡單實現幾種常見的前端效果,附程式碼!前端
- 電腦出現常見問題卡頓,教你優化提速技巧優化
- git 常見問題及操作方法Git
- WordPress:常見問題及解決方案
- Nacos 常見問題及解決方法
- mybatis常見庫及問題彙總MyBatis
- rocketmq常見問題及使用 新手篇MQ
- UltraEdit常見問題及解決教程
- 前端常見的安全問題及防範措施前端
- 高匿代理的介紹及常見問題
- 面試常見問題之實現bind函式面試函式
- 常見排序原理及 python 實現排序Python
- 常見問題
- 常見問題03:php實現海報生成(包含多行文字與水印)PHP
- 快取常見問題及解決方案快取
- 爬蟲常見問題及解決方式爬蟲
- 必知的git基本命令及常見問題Git
- SpringCloud之Eureka的常見問題及配置優化SpringGCCloud優化
- 離線批次資料通道Tunnel的最佳實踐及常見問題
- 常見的js演算法面試題收集,es6實現JS演算法面試題
- React + Typescript領域初學者的常見問題和技巧ReactTypeScript
- 【效能測試】常見的效能問題分析思路(二)案例&技巧
- 關於CleanMyMac常見問題與解答Mac
- web開發技巧-網頁排版佈局常見問題及解決辦法Web網頁