您的位置:首页 > 编程语言 > C语言/C++

【算法导论】笔记一(插入排序、归并排序、选择排序、Peek Finding、加法器、中英文字符串搜索)

2016-12-17 22:32 609 查看
在一周学习MIT算法导论课程的时候,课上和书上都碰到了一些算法,我自己一一用C++进行了实现。本文中“中英文字符串搜索”不是算法导论的内容,是一个童鞋问我的的问题。由于MIT课程的关系,注释部分我就直接用英文啦。

由于刚开始学,代码优化做的还不到位,我会逐渐逐渐进行改善。

一、插入排序

算法最坏时间复杂度(已经尝试证明):θ(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");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐