【算法导论】笔记一(插入排序、归并排序、选择排序、Peek Finding、加法器、中英文字符串搜索)
2016-12-17 22:32
609 查看
在一周学习MIT算法导论课程的时候,课上和书上都碰到了一些算法,我自己一一用C++进行了实现。本文中“中英文字符串搜索”不是算法导论的内容,是一个童鞋问我的的问题。由于MIT课程的关系,注释部分我就直接用英文啦。
由于刚开始学,代码优化做的还不到位,我会逐渐逐渐进行改善。
在合并的时候没有采用插入“底牌”方法,而是直接通过数组长度来进行判断了,比较蛋疼。
贪心法中加了点花头:没有按照正常顺序去找,而是把数组按照逆时针顺序输出。实现的不是很好,四个边缘点上的处理有点小复杂。网上查了查最好的办法是用递归,这样逻辑更加清晰点。
然后就是为了复习C,木有用vector和allocator,尝试用malloc和指针去指派动态数组。呵呵,复杂度增加不少。C++还是多用vector。
基本思路是:先找第一个字符,如果第一个字符匹配则匹配第二个字符,第二个匹配则匹配第三个字符……
由于刚开始学,代码优化做的还不到位,我会逐渐逐渐进行改善。
一、插入排序
算法最坏时间复杂度(已经尝试证明):θ(n^2)/* Input:A set of number Output:A ordered number */ vector<int>& insertSort(vector<int>& v) { for (int i = 0; i < v.end() - v.begin() - 1; i++) { for (int j = 0; j < i + 1; j++) { cout << i + 1 - j << " compared with " << i - j << endl; if (v.at(i + 1 - j) > v.at(i - j)) { int temp = v.at(i + 1 - j); *(v.begin() + i + 1 - j) = v.at(i - j); *(v.begin() + i - j) = temp; } } } return v; }
二、归并排序
算法最坏时间复杂度(已经尝试证明):θ(nlg(n))在合并的时候没有采用插入“底牌”方法,而是直接通过数组长度来进行判断了,比较蛋疼。
void printVector(const std::vector<int>& v, std::string s, bool newLine) { std::cout << s << " "; for (int i : v) { std::cout << i << " "; } std::cout << (newLine ? "\n" : "|"); } std::vector<int> mergeVector(std::vector<int>& mergedLeft, std::vector<int>& mergedRight) { printVector(mergedLeft, " MergedLeft:", false); printVector(mergedRight, " MergedRight:", true); std::vector<int> result; int leftIndex = 0; int rightIndex = 0; while (true) { if (leftIndex < mergedLeft.size() && rightIndex < mergedRight.size()) { if (mergedLeft.at(leftIndex) > mergedRight.at(rightIndex)) { result.insert(result.end(), mergedLeft.at(leftIndex)); leftIndex += 1; } else { result.insert(result.end(), mergedRight.at(rightIndex)); rightIndex += 1; } } else { break; } } //insert the rest for (int i = leftIndex; i < mergedLeft.size(); i++) { result.insert(result.end(), mergedLeft.at(i)); } for (int i = rightIndex; i < mergedRight.size(); i++) { result.insert(result.end(), mergedRight.at(i)); } printVector(result,"merged Vector:",true); return result; } std::vector<int> merge_sort(std::vector<int>& v) { int middle = v.size() / 2; //divide if (v.size() != 1) { std::vector<int> leftHalf; std::vector<int> rightHalf; leftHalf.insert(leftHalf.begin(), v.begin(), v.begin() + middle); rightHalf.insert(rightHalf.begin(), v.begin() + middle, v.end()); std::vector<int> mergedLeft = merge_sort(leftHalf); std::vector<int> mergedRight = merge_sort(rightHalf); return mergeVector(mergedLeft, mergedRight); } return v; }
三、选择排序
算法最坏时间复杂度(已经尝试证明):θ(n^2)#define MAX_INT (pow(2,(sizeof(int)*8)-1)-1) void selection_sort(int* intArray, int n) { for (int i = 0; i < n - 1; i++) { counter++; int min = MAX_INT; int minIndex = 0; for (int j = i + 1; j < n; j++) { couter1++; cout << "i=" << i << " j=" << j << endl; //find the min value if (intArray[j] < min) { min = intArray[j]; minIndex = j; } if (j == n - 1) { int temp = intArray[i]; intArray[i] = min; intArray[minIndex] = temp; } } } }
四、Peek Finding
分别实现了1D数组(使用分治法),2D数组(使用分治法、贪心法分别实现)贪心法中加了点花头:没有按照正常顺序去找,而是把数组按照逆时针顺序输出。实现的不是很好,四个边缘点上的处理有点小复杂。网上查了查最好的办法是用递归,这样逻辑更加清晰点。
然后就是为了复习C,木有用vector和allocator,尝试用malloc和指针去指派动态数组。呵呵,复杂度增加不少。C++还是多用vector。
#include <iostream> #include <initializer_list> struct point { int row; int column; int value; }; /* * peekFinding using divide and conquer algorism introduced * in the lecture */ point peekFinding1D(std::initializer_list<int> src) { int begin = 0; int end = src.end() - src.begin() - 1; while (true) { int middle = (end + begin) / 2; std::cout << "middle:" << *(src.begin() + middle) << std::endl; if (*(src.begin() + middle) <= *(src.begin() + middle + 1)) { //throw away the left side,there might be a peek on the right //side begin = middle + 1; middle = (end + begin) / 2; } else if (*(src.begin() + middle) <= *(src.begin() + middle - 1)) { //throw away the right side,there might be a peek on the left //side end = middle + 1; middle = (end + begin) / 2; } else { return point{0,middle,*(src.begin() + middle)}; } } } point maximumOfRow(int* row, int length) { int max = -999; int position; for (int i = 0; i < length; i++) { if (*(row + i) > max) { max = *(row + i); position = i; } } return point{0,position,max}; } /* * peekFinding using divide and conquer algorism introduced * in the lecture */ point peekFinding2D(int** srcArray, int rows, int columns) { int y = 0; int height = rows - 1; while (true) { int middleRow = y + (height) / 2; //Find the maximum number and it position point p = maximumOfRow(*(srcArray + middleRow), columns); int column = p.column; if (middleRow != rows - 1 && middleRow != 0) { if (*(*(srcArray + middleRow) + column) <= *(*(srcArray + middleRow - 1) + column)) { y = middleRow - 1; height /= 2; //leave the right half } else if (*(*(srcArray + middleRow) + column) <= *(*(srcArray + middleRow + 1) + column)) { y = middleRow + 1; height /= 2; //delete the right half } else { //founded return point{middleRow,column,*(*(srcArray + middleRow) + column)}; } } else { point p = maximumOfRow(*(srcArray + middleRow), columns); return point{middleRow,p.column,p.value}; } } } /* * peekFinding using greedy method */ point peekFinding2DGreedy(int** srcArray, int rows, int columns) { //-1=left 1=right 0=don't move on x axis int directionX = 0; //-1=up 1=down 0=don't move on y axis int directionY = 0; int currentRow = 0; int currentColumn = 0; int boundX = 0;//record which circle it had entered int boundXLimit; if (columns % 2 == 0) { boundXLimit = columns / 2 - 1; } else { boundXLimit = (columns + 1) / 2 - 1; } int boundY = 0; int boundYLimit = 0; if (rows % 2 == 0) { boundYLimit = rows / 2 - 1; } else { boundYLimit = (rows + 1) / 2 - 1; } bool justStart = true; for (int i = 0; i < rows * columns; i++) { currentRow += directionY; currentColumn += directionX; if (currentRow == boundX && currentColumn == boundX) { //top-left-corner directionX = 0; directionY = 1; if (!justStart) { if (boundX < boundXLimit) { boundX += 1; } if (boundY < boundYLimit) { boundY += 1; } currentColumn += 1; currentRow += 1; justStart = true; } } else if (currentRow == boundX && currentColumn == columns - 1 - boundX) { //top-right-corner directionX = -1; directionY = 0; justStart = false; } else if (currentRow == rows - 1 - boundX & currentColumn == boundX) { //bottom-left-corner directionX = 1; directionY = 0; } else if (currentRow == rows - 1 - boundX && currentColumn == columns - 1 - boundX) { //bottom-right-corner directionX = 0; directionY = -1; } /* * check if it is the peek */ if ((currentColumn == 0 || (*(*(srcArray + currentRow) + currentColumn) >= *(*(srcArray + currentRow) + currentColumn - 1))) && (currentColumn == rows - 1 || (*(*(srcArray + currentRow) + currentColumn) >= *(*(srcArray + currentRow) + currentColumn + 1))) && (currentRow == rows - 1 || (*(*(srcArray + currentRow) + currentColumn) >= *(*(srcArray + currentRow + 1) + currentColumn))) && (currentRow == 0 || (*(*(srcArray + currentRow) + currentColumn) >= *(*(srcArray + currentRow - 1) + currentColumn)))) { return point{currentRow,currentColumn, *(*(srcArray + currentRow) + currentColumn)}; } } } void main() { auto result = peekFinding1D({1, 9, 7, 4, 5}); std::cout << "1DDivideAndCounquer result[" << result.column << "]=" << result.value; std::cin.get(); int cache[5][4] = {{1 ,14 ,13 ,12}, {2 ,15 ,20 ,11}, {3, 16, 19, 10}, {4, 17, 18, 9}, {5, 6, 7, 8}}; int** srcArray = (int**)calloc(sizeof(int**), 5); for (int row = 0; row < 5; row++) { *(srcArray + row) = (int*)calloc(sizeof(int*), 4); for (int column = 0; column < 4; column++) { *(*(srcArray + row) + column) = cache[row][column]; } }; auto result2D = peekFinding2D(srcArray, 5, 4); std::cout << "2DDevideAndCounquer result[" << result2D.row << "][" << result2D.column << "]=" << result2D.value; std::cin.get(); auto result2DGreedy = peekFinding2DGreedy(srcArray, 5, 4); std::cout << "2DGreedy result[" << result2DGreedy.row << "][" << result2DGreedy.column << "]=" << result2DGreedy.value; std::cin.get(); for (int row = 0; row < 5; row++) { free(*(srcArray + row)); }; free(srcArray); }
五、加法器
switch语句的使用让代码复杂了一些,比较愚笨。看了答案,用的是一种比较巧妙的方法算出了进位的数字(carry)和留下的数(current)/* * Input:Tow binary arrays * Output:A decimal number=(bin1+bin2) */ std::vector<int> manuallySumTwoBinaryNumber(std::vector<int> binArray1, std::vector<int> binArray2) { int bA1 = binArray1.end() - binArray1.begin(); int bA2 = binArray2.end() - binArray2.begin(); const int max = bA1 < bA2 ? bA2 : bA1; std::allocator<int> allocator; int carry = 0; std::vector<int> binArr1(max - bA1, 0); std::vector<int> binArr2(max - bA2, 0); std::vector<int> result(max + 1); for (int i:binArray1) { binArr1.push_back(i); } for (int i : binArray2) { binArr2.push_back(i); } int i = 0; for (i = max - 1; i >= 0; i--) { int sum = binArr1[i] + binArr2[i] + carry; int current = 0; switch (sum) { case 0: carry = 0; current = 0; break; case 1: carry = 0; current = 1; break; case 2: carry = 1; current = 0; break; case 3: carry = 1; current = 1; break; } result[result.size() - 1 - i] = current; } result[result.size() - 1] = carry; return result; }
六、中英文混合子字符串查找
手工实现了从一个字符串里面查找一个中英文混合的子字符串。没有用到相关的标准库函数。基本思路是:先找第一个字符,如果第一个字符匹配则匹配第二个字符,第二个匹配则匹配第三个字符……
#include <stdio.h> #include <stdlib.h> int strLen(char* begin) { int len = 0; while (true) { if (*(begin + len) != '\0') { ++len; } else { return len; } } } void seperateChiAndEng(char* begin, char* end) { char* cur = begin; while (cur != end) { if (*cur >= 0) { //english printf("%c\n", *(cur++)); } else { char cWord[3]; cWord[0] = *(cur++); cWord[1] = *(cur++); cWord[2] = '\0'; //chinese printf("%s\n", cWord); } } } bool isChi(char* str) { if (*str > 0) { return false; } else { return true; } } int findStr(char* src, char* str, int strLen) { int position = -1; char* pointer = src; char* firstWord; int firstWordLen = 0; int counter = 0; if (isChi(pointer)) { firstWordLen = 2; char cache[2]; firstWord = cache; firstWord[0] = *str; firstWord[1] = *(str + 1); } else { firstWordLen = 1; char cache[1]; firstWord = cache; firstWord[0] = *str; } while (*pointer != '\0') { for (int i = 0; i < firstWordLen; i++) { if (*(pointer + i) == *(firstWord + i)) { ++counter; } } if (firstWordLen == counter) { int matched = true; //founded the first word,check the rest of the word position = pointer - src; for (int j = 0; j < strLen; j++) { if (*(pointer + j) != *(str + j)) { matched = false; break; } } if (!matched) { position = -1; } else { break; } } else { ++pointer; counter = 0; //didn't found the first word } } return position; } void main() { printf("请输入源string(最长不超过255字节)\n"); char src[255]; scanf_s("%s", src, 255); printf("请输入要查找的string(最长不超过255字节):\n"); char target[255]; scanf_s("%s", target, 255); int position = findStr(src, target, strLen(target)); printf("您要查找的字符串在第%d个字节位置\n", position); system("pause"); }
相关文章推荐
- 算法导论之插入排序,选择排序,归并排序,冒泡排序,希尔排序,堆排序,快速排序的c语言实现
- 基本的排序算法:冒泡排序、插入排序、希尔排序、选择排序、归并排序、快速排序、堆排序
- 笔试面试最常涉及到的12种排序算法(包括插入排序、二分插入排序、希尔排序、选择排序、冒泡排序、鸡尾酒排序、快速排序、堆排序、归并排序、桶排序、计数排序和基数排序)进行了详解。每一种算法都有基本介绍、算
- 几种常见排序算法之Java实现(插入排序、希尔排序、冒泡排序、快速排序、选择排序、归并排序)
- 算法分析中最常用的几种排序算法(插入排序、希尔排序、冒泡排序、选择排序、快速排序,归并排序)C 语言版
- 常用算法--基本排序算法(冒泡排序,选择排序,插入排序,快速排序,归并排序,桶排序)
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法,而冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 几种常用的排序算法的分析及java实现(希尔排序,堆排序,归并排序,快速排序,选择排序,插入排序,冒泡排序)
- 算法导论学习笔记(六):计数排序与基数排序
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结
- 算法导论_选择排序
- 8 排序--选择排序,插入排序,冒泡排序,shell排序,快速排序(递归,迭代,改进版本),归并排序
- 选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法, 冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。
- 【学习笔记】python版选择排序和插入排序及时间复杂度分析
- 冒泡排序,选择排序,插入排序,堆排序,归并排序,快速排序
- 快速排序、希尔排序、插入排序、选择排序、归并排序、堆排序总结
- 几种基本的排序算法:选择排序、插入排序、冒泡排序
- 各种排序算法总结----基数排序、归并排序、插入排序、冒泡排序、选择排序、快速排序、堆排序、希尔排序
- 【算法导论】 2.2选择排序