1.给定一个int型数组,某个数字在该数组中出现的次数超过了一半,请找出出现次数超过一半的这个数字,要求时间复杂度为O(n),空间复杂度为O(1):
欢迎对该题进行批评和提出其他新的解法,或者对该题进行优化,无论代码逻辑还是代码风格。
上代码:
int number_beyond_half(int numbers[], int n)
{
int result;
int count=0;
for(int i=0; i<n; i++)
{
if(count==0)
{
result = numbers[i];
count++;
}
else if(result==numbers[i])
{
count++;
}
else
{
count--;
}
}
return result;
}
理清思路:
因为该题要求时间复杂度为O(n),所以最多只能有一层循环。考虑到这样的一种情形,因为某个数字超过了一半,所以用一个该数字“消灭”一个不同的数字,剩下的最后那个数字肯定是出现次数超过一半的数字了。在这里我们依次扫描数组的每一项,用result保存可能的结果,count记录其剩余的次数(每去’消灭‘一个数字,自己也牺牲了一个,所以count--),result与当前扫描结果对比,如果相同,就说明该数字又多了一次,count++;如果不相等,就’消灭‘它,自身牺牲一个,所以count--;但是如果count==0,则说明result自身都牺牲完了,所以下次扫描到的数字作为可能的结果result。
注意,前提是该数组中确实有某个数字出现的次数超过了一半,本算法不考虑在数组中没有出现次数超过一半的数字这种情况,因为题目已经说明了有某个数字出现的次数超过了一半。另外该题还有一种解法,就是使用栈,原理差不多,有兴趣的同学自己去码一码吧,不过不能够满足空间复杂度为O(1)。
2.反转链表,例如有这样的链表A->B->C->D->NULL,反转之后变成D->C->B->A->NULL。注意,要求直接在原链表上进行反转。
这里有三种解法,第一种是直接循环链表,第二种是采用递归方式,第三种是采用堆栈的方式,相比而言,采用递归稍微难一点。欢迎对该题进行指正和提出其他新的解法,也可以对本博文其他题提出你们宝贵的意见。
上代码:
链表的数据结构定义:
typedef struct Node
{
char data;
Node* next;
}List;
第一种:直接循环访问链表方式:
List* reverse1(List* list)
{
if(list==NULL)
{
return NULL;
}
Node* s = list;
Node* t = list->next;
s->next = NULL;
while(t !=NULL)
{
Node* tmp = t->next;
t->next = s;
s = t;
t = tmp;
}
return s;
}
第二种:递归方式:
递归函数:
List* reverse_help(List* list, List*& head)
{
if(list->next==NULL)
{
if(head==NULL)
{
head = list;
}
return list;
}
Node* node = reverse_help(list->next, head);
node->next = list;
list->next = NULL;
return list;
}
反转链表函数:
List* reverse2(List* list)
{
if(list == NULL)
{
return NULL;
}
List* head=NULL;
reverse_help(list, head);
return head;
}
第三种:堆栈方式:
List* reverse3(List* list)
{
if(list==NULL)
{
return NULL;
}
Node* stack[100];
int top = -1;
while(list!=NULL)
{
top++;
stack[top]=list;
list=list->next;
}
List* ret = stack[top];
top--;
Node* s = ret;
while(top!=-1)
{
Node* node = stack[top];
top--;
s->next = node;
s = node;
}
s->next = NULL;
return ret;
}
上面的递归方式在确定新的头结点的部分可以进行优化;该链表没有形式化的头结点,就是说list指向的就是第一个有用的结点了(他的数据域有意义的,一些书上的都是有一个形式化的头结点的,头结点中的data域没有实际意义);为了一些同学方便测试和改进,现将一些可能用到的代码也端上来:
从数组中构建单链表:
List* getListFromArray(char array[], int n)
{
if(array==NULL || n<=0)
{
return NULL;
}
List* list=NULL,*s=NULL;
for(int i=0; i<n; i++)
{
Node* node = (Node*)malloc(sizeof(Node));
node->data = array[i];
node->next = NULL;
if(s==NULL)
{
list=s=node;
}
else
{
s->next = node;
s = node;
}
}
return list;
}
正向遍历输出单链表:
void output(List* list)
{
while(list != NULL)
{
printf("%c ",list->data);
list = list->next;
}
}
3.一个二维数组,每一行按照从左到右递增的顺序排列,每一列按从上到下递增的顺序排列,要求完成一个函数,输入这样的一个二维数组和整数,判断数组中是否含有该整数。提示:含有该整数函数返回1,否则返回0。二维数组的行数和列数不确定,所以采用int指针表示,函数原型如下:int findNumFromMatrix1(int* matrix, int rows, int columns, int number);matrix表示指向二维数组的指针,rows为它的行数,columns表示它的列数,number为待查找的整数。
先上代码:
int findNumFromMatrix1(int* matrix, int rows, int columns, int number)
{
int ret = 0;
if(matrix==NULL || rows<1 ||columns<1)
{
return ret;
}
int row = 0;
int column = columns -1;
while(row<rows && column>-1)
{
int index = row*columns+column;
if(matrix[index] == number)
{
ret = 1;
break;
}
if(matrix[index] < number)
{
row++;
}
else
{
column--;
}
}
return ret;
}
思路解析:该题最简单除暴的解法就是对二维数组直接遍历,但是这样时间效率不高。考虑到这里二维数组的特性,按行和按列都是有序的,所以选择与数组最右上角的元素比较,这样就可以每次排除一行或者一列,同理,也可以与数组最左下角元素进行比较。如果二维数组是个n*n的数组,那么直接遍历查找的时间复杂度是O(n^2),而用该方法,时间复杂度就变成了O(n)。
4.题目:实现一个函数,把字符串中的每个空格替换成“%20”。例如输入We are happy,则替换后的字符串为We%20are%20happy,要求在原字符串上面进行操作。假设所给字符串后面有足够的内存来存放替换空格后的字符串。 函数原型:void replace_blank(char string[]);
上代码:
void replace_blank(char string[])
{
if(string==NULL)
{
return;
}
int s = 0;
int t = 0;
int blank_num = 0;
//将s指向字符串最后一个字符,并计算出字符串有多少个空格
while(string[s]!='\0')
{
if(string[s]==' ')
{
blank_num++;
}
s++;
}
//将t指向新字符串最后一个字符。
t=s+2*blank_num;
while(s!=t)
{
if(string[s]==' ')
{
string[t] = '0';
string[t-1] = '2';
string[t-2] = '%';
t-=2;
}
else
{
string[t] = string[s];
}
s--;
t--;
}
}
void replace_blank(char* string)
{
if(string==NULL)
{
return;
}
char* s = string;
char* t = NULL;
int blank_num = 0;
//将s指向字符串最后一个字符,并计算出字符串有多少个空格
while(*s!='\0')
{
if(*s==' ')
{
blank_num++;
}
s++;
}
//将t指向新字符串最后一个字符。
t=s+2*blank_num;
while(s!=t)
{
if(*s==' ')
{
*t = '0';
t--;
*t = '2';
t--;
*t = '%';
}
else
{
*t = *s;
}
t--;
s--;
}
}
思路解析:由于该题是要求在原字符串上操作,如果我们从字符串开始位置做替换,那么每次遇到一个空格后,空格后面的所有字符都需要向后移动两个位置,而且移动的过程中必须先保存后面的部分字符以免被覆盖,另外每次遇到空额后又需要移动,所以这样的方式空间复杂度和时间复杂度都不够好。我们可以转换一下思维,从字符的后面开始移动字符。首先我们用一个索引s来扫描字符串,找出字符串中的空格数blank_num,而且将s指向字符串最后一个字符,即‘\0’的位置。由于每替换一个空格字符串就增加两个字符,所以就可以计算出替换后的字符串总共有多少个字符,可以计算出替换后的字符串的最后一个字符位置t,然后就可以将s处的字符复制到t处,当s处的字符是空格时,t处就替换为"%20",s和t分别移动到他们的前一个字符位置,继续复制,直到s==t,这时所有空格已经替换完毕。具体还是看代码吧,这里没有图说的不是很清楚。
5.反向输出链表,要求不能改变链表结构。
上代码:
void print_list_reversingly(List* list)
{
if(list!=NULL)
{
print_list_reversingly(list->next);
printf("%c ",list->data);
}
}
思路解析:(这里的链表为单链表,链表结构在该文前面可以找到)该题是个典型的递归,要输出当前节点的值必须先输出next指向的节点的值(递归),也可以使用堆栈,一般来说,可以使用递归的都可以使用堆栈来完成,递归本来就是一种堆栈结构。
6.从1到n整数中1出现的次数。
解法一:
int NumberOf1BetwteenAndN(unsigned int n)
{
int number = 0;
for(unsigned int i = 1;i<=n;i++)
{
number += NumberOf1(i);
}
return number;
}
int NumberOf1(unsigned int n)
{
int number = 0;
while(n)
{
if( n%10 == 1 )
number ++ ;
n = n/10 ;
}
return number;
}
上述解法中,对1到n中的每个数字都要做除法和求余运算以求出该数字中1出现的次数,时间复杂度约为O(n*logn)。
解法二:
//求10的N次方,可以使用库函数pow代替
int PowerBase10(unsigned int n)
{
int result = 1;
for(unsigned int i = 0; i<n; i++)
{
result*=10;
}
return result;
}
int number_of_1_v1(const char* strN)
{
if(*strN == '\0' || !*strN || *strN <'0' ||*strN >'9')
{
return 0;
}
int first = *strN - '0';
unsigned int length = static_cast<unsigned>(strlen(strN));
if(length == 1 && first == 0)
{
return 0;
}
if(length == 1 && first >0)
{
return 1;
}
int numFirstDigit = 0;
if(first >1)
{
numFirstDigit = PowerBase10(length - 1);
}
else if(first == 1)
{
numFirstDigit = atoi(strN + 1) + 1;
}
return numFirstDigit + first*(length-1)*PowerBase10(length-2)+ number_of_1_v1(strN+1);
}
该解法来自《剑指offer》,下面的解法三也是受该解法启发。
解法三:
/**num是表示在当前位(不包括当前位)之前的所有字符组合起来表示的十进制整数,初始时为0**/
int number_of_1_v2(int num, const char* strN)
{
if(*strN == '\0' || !*strN || *strN <'0' ||*strN >'9')
{
return 0;
}
int first = *strN - '0';
unsigned int length = static_cast<unsigned>(strlen(strN));
if(length == 1 && first == 0)
{
return 0;
}
if(length == 1 && first >0)
{
return 1;
}
int numFirstDigit = 0;
if(first >1)
{
numFirstDigit = PowerBase10(length - 1);
}
else if(first == 1)
{
numFirstDigit = atoi(strN + 1) + 1;
}
num = 10*num + first; //num表示从第一位到当前位(包括当前位)所表示的数字
return numFirstDigit + num*PowerBase10(length-2) + number_of_1_v2(num, strN+1);
}
把每一位出现1的次数相加。
——敬请期待,持续更新中。