每日算法之2

每日算法之2

1.之兩個非遞減的數組合併爲一個數組保持依然有序

	//問題描述:兩個非遞減數組,A,B,A具有足夠的內存容納B,要求將兩個數組合併爲一個數組,保持數組依然非遞減

//算法分析:從B數組中第一個元素開始,依次與數組A的尾部至頭部開始掃描比較,如果小於,則後移一位,下標前移一位,繼續比較,直到找到非小於的位置,將其插入當前的後一位;遍歷完B數組,則循環結束

//算法實現;
void mergeSort(int A[],int B[],int a_length,int b_length){
    if(A==NULL || B==NULL ){
        cout<<"error!"<<endl;
        return;
    }
    if(a_length>b_length){
        int j=a_length-b_length-1;//表示指向A數組的下標
        int i;//表示指向B數組的下標
        int k=a_length-1;
        for(i=b_length-1;i>=0 && j>=0;--i){//表示遍歷B數組的元素
            for(j;j>=0;--j){
                if(B[i]>=A[j]){
                    A[k--]=B[i];
                    break;                        
                }                
                else
                    A[k--]=A[j];
            }
        }
        if(i+1>=0){//將B數組剩餘元素從頭開始依次填入A數組,記得由於上次循環退出時,是自動減了1 
            for(int p=0;p<=i+1;++p){
                A[p]=B[p];
            }
        }              
    }
}


int main(){
	//測試用例
	int a[10]={4,5,6,8,10,34};
	int b[4]={3,5,9,18}; 
	mergeSort(a,b,10,4);
	for(int i=0;i<10;++i)
		cout<<a[i]<<" ";	
} 
//雖然使用了兩個循環,但是沒執行一次比較,必會幹掉一個最大值放在後面,也就是說,從思路上就能判斷出基本操作次數最壞情況下爲兩個序列的長度和,所以時間複雜度爲O(n),即線性時間


//其他優秀的算法代碼分析:
/**
     * 合併兩個有序數組,合併後仍然有序
     * @param a 要合併的數組A
     * @param b 要合併的數組B
     * @param c 合併後的數組C
     */
void merge(int a[] ,int b[],int c[]){
        //1.傳入函數的參數必須考慮周全,傳入什麼參數,有什麼作用,考慮是值傳遞還是引用傳遞,需不需
        	//要加const常量進行限定
        int lengthA = a.length;
        int lengthB = b.length; 
        //2.首先將此函數需要使用的局部變量定義好,標識符要有實際意義,儘量避免使用i,k,l,否則
        	//閱讀你的代碼真是一件頭疼的事情
        
        int indexA = 0;
        int indexB = 0;
        int indexC = 0;
        
        //3.使用循環必須充分考慮好退出條件,是否存在其他退出循環的情況
        while(indexA < lengthA && indexB < lengthB){
            if(a[indexA] < b[indexB]){
                c[indexC++] = a[indexA++];
            }else{                
                c[indexC++] = b[indexB++];
            }
        }
        //4.當退出循環時,這個遍歷指針或者下標是否自動前移?
        	//此外,當循環結束,是否真的完成了?
        while(indexA < lengthA){
            c[indexC++] = a[indexA++];            
        }
        while (indexB < lengthB) {
            c[indexC++] = b[indexB++];            
        }
        //5.使用循環最頭疼的就是邊界條件,只要記住一點,編號,第幾號和個數,第幾個不要混用就OK了
    }

2.之鏈表操作-反向輸出鏈表

鏈表的特點:是一種動態的數據結構,傳入鏈表時,只需要傳入頭結點指針即可,不需要指定長度

//問題描述:輸入一個鏈表,將一個帶頭結點的鏈表反轉輸出
	//第一種方法
struct LNode{
    int data;
    struct LNode *next;
};
void reverse_list(LNode *A){
    LNode *curLNode,*nextLNode,*tempLNode;//定義當前指針和下一個指針以及一個臨時指針
    
    curLNode=A->next;
    nextLNode=curLNode->next;//進行初始化操作
    
    curLNode->next=NULL;
    while(nextLNode!=NULL){//當下一個節點爲空時,退出循環
        tempLNode=curLNode;
        curLNode=nextLNode;
        nextLNode=nextLNode->next;//每次反轉之前需要後移一位,並使用臨時變量記住上次的節點
        
        curLNode->next=tempLNode;//將當前的節點指針反轉指向臨時節點
    }
    A->next=curLNode;//最後將頭結點指針重新指向反轉後的第一個節點
}


void createList(LNode *A,const int data[],int length){
    //1.使用頭插法創建一個鏈表,並將一個數組賦值給鏈表節點的數據域,由於使用的是頭插法,所以數組的數據順序
	//是逆序的!!
	LNode *firstLNode;//定義一個指向鏈表第一個節點的指針
    LNode *newLNode;//定義一個需要插入個新節點指針
    
    A=new LNode;//由於傳入的只是一個指向節點的指針,所以創建時必須new出一個指針,並指向它!!!
    A->next=NULL;//初始化頭結點指針
    
    for(int i=0;i<length;++i){
        newLNode= new LNode;
        newLNode->data=data[i];
        
        firstLNode=A->next;
        newLNode->next=firstLNode;
        A->next=newLNode;//第一次雖然指向空節點,但正好將尾節點初始化爲NULL,一舉兩得!!
    }   
    
    //2.使用尾插法正向插入數組數據
    LNode *lastLNode;
    LNode *newLNode;
    
    A=new LNode;
    A->next=NULL;
    
    lastLNode=A;//初始化尾節點
    for(int i=0;i<length;++i){
		newLNode= new LNode;
        newLNode->data=data[i];
        
        lastLNode->next=newLNode;
        lastLNode=lastLNode->next;
    }
    lastLNode->next=NULL;//插入完畢必須初始化尾節點指針域
}

//打印鏈表
void printfList( LNode *A){
    LNode *index;
    for(index=A;index->next!=NULL;index=index->next){//由於是帶頭結點的鏈表
        cout<<index->next->data<<" ";//index爲當前指針節點,但是打印輸出時卻是下一個節點
    }
    cout<<endl;
}
//測試用例:
int main(){
	
	int a[10]={1,2,3,4,5,6,7,8,9,10};
	LNode *list;
	createList(list,a,10);
	printfList(list);
    
	reverse_list(list);
	printfList(list);
		 
}
		//第二種方法
//第一種方法需要修改鏈表,如果要求不能修改數據結構,改變策略,由於要求將鏈表逆序輸出,可以先正向遍歷
	//將其存入一個棧中,利用棧的後進先出的特點,從而實現逆序
//算法實現:
void reversePrintfList_useListStack(LNode *A){//必須使用一個鏈表棧實現
    LNode *indexA,*indexS;//鏈表A和S的指針迭代器
    LNode *stack_t_LNode;//棧的頭結點
    LNode *new_s_LNode;//棧的新插入的節點
    LNode *first_s_LNode;//表示棧的第一個節點
    
    stack_t_LNode=new LNode;
    stack_t_LNode->next=NULL;//初始化鏈表棧,必須使用頭插法
    
    for(indexA=A;indexA->next!=NULL;indexA=indexA->next){
        new_s_LNode=new LNode;
        new_s_LNode->data=indexA->next->data;//壓入棧
        
        first_s_LNode=stack_t_LNode->next;
        new_s_LNode->next=first_s_LNode;
        stack_t_LNode->next=new_s_LNode;//使用頭插法連接到棧中
    }
    
    //將棧遍歷輸出
    for(indexS=stack_t_LNode;indexS->next!=NULL;indexS=indexS->next){
		cout<<indexS->next->data<<" ";
    }
    cout<<endl;
    delete[] stack_t_LNode;
}

		//第三種方法
//既然使用自定義鏈表棧實現反向輸出,爲何不使用一個簡單的遞歸函數,直接兩三行代碼實現遞歸棧的反轉輸出
void reversePrintfList_useRecursion(LNode *A){
    if(A!=NULL){
        if(A->next!=NULL){
            reversePrintfList_useRecursion(A->next);
        }
        cout<<A->data<<" ";
    }
}//該辦法有一個缺點,如果將帶頭結點的鏈表傳入,頭結點的data域也會被訪問打印,解決辦法只能再調用的時候,將頭結點過濾掉!!!
//比如:
reversePrintfList_useRecursion(A->next);

3.之二叉樹的三種遍歷考察[重點]

//掌二叉樹的八大遍歷實現,構建;;掌握完全二叉樹搜索樹,最大小堆,紅黑樹,字典樹		

		//使用先序遍歷構建二叉樹
typedef struct BTNode{
    char data;
    struct BTNode *lchild,*rchild;
}BTNode;

//邏輯存在嚴重的錯誤!!
//算法分析:使用先序遍歷將字符串數組"ABDG##H###CE##F##"構建成一棵二叉樹
void createBTree_usePreorder(BTNode *B,char *data){

    int index_data;
    
    index_data=0;
    
    if(data[index_data]!='\0'){
        if(data[index_data]=='#')
            B=NULL;//表示爲葉節點
        else{
            B=new BTNode;
            B->data=data[index_data];
            createBTree_usePreorder(B->lchild,data+1);
            createBTree_usePreorder(B->rchild,data+1);
        }
    }
}



		//二叉樹的四種遍歷方式:三種深度優先的遞歸實現和藉助棧的非遞歸實現;
		//一種藉助隊列實現廣度優先的非遞歸實現
//1.先序遍歷的遞歸實現和非遞歸實現
//遞歸實現:
void BTree_preorder(BTNode *B){
    if(B!=NULL){
        cout<<B->data<<" ";
        BTree_preorder(B->lchild);
        BTree_preorder(B->rchild);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章