---------------------------------------------------------------------------
1. 链表和数组的区别在哪里?
数组静态分配内存,链表动态分配内存;
数组在内存中连续存放,链表不连续;
数组可以用下标定位,时间复杂度 O(1),链表定位元素时间复杂度 O(n);
数组删除或插入元素时间复杂度 O(n),链表时间复杂度 O(1);
---------------------------------------------------------------------------
2. 编写实现链表排序的一种算法。说明为什么你会选择用这样的方法?
可用排序方法 (考虑单向链表):归并,插入、冒泡、选择。
(考虑双向链表):归并、快排,插入、冒泡、选择。
优先考虑归并,时间复杂度低,实现简单。
也可以先建立一个数组,把链表里的指针都放进去,然后排序,最后重建链表。
---------------------------------------------------------------------------
3. 编写实现数组排序的一种算法。说明为什么你会选择用这样的方法?
可用排序方法:基数 (O(n),对元素有限制),归并、快排、堆 (O(nlogn),插入、冒泡、选择(O(n^2))。
如果不考虑实际应用需要,优先考虑归并,时间复杂度低,实现简单。
实际应用中,需要根据实际情况选择:若数据元素个数有限,考虑基数;若数据随机,考虑快排,因为随机情况下快排平均复杂度低;若数据接近有序,考虑归并。
---------------------------------------------------------------------------
4. 请编写能直接实现 strstr 函数功能的代码。
/* 实现一。 */
char * strstr(const char * content, const char * pattern)
{
const char * p_content;
const char * p_pattern;
if (NULL == content || NULL == pattern)
{
return NULL;
}
p_content = content;
p_pattern = pattern;
while (*p_content && *p_pattern)
{
if (*p_content == *p_pattern)
{
++p_content;
++p_pattern;
}
else
{
p_content = p_content - (p_pattern - pattern) + 1;
p_pattern = pattern;
}
}
if (0 == *p_pattern)
{
return p_content - (p_pattern - pattern);
}
else
{
return 0;
}
}
/* 实现二 (KMP)。 */
#include <stdlib.h>
char * strstr(const char * content, const char * pattern)
{
int i;
int j;
int len;
int * next;
if (NULL == content || NULL == pattern)
{
return NULL;
}
len = 0;
while (pattern[len])
{
++len;
}
next = (int *)malloc(len * sizeof(int));
next[0] = -1;
i = 0;
j = -1;
while (pattern[i])
{
if (-1 == j || pattern[i] == pattern[j])
{
++i;
++j;
if (pattern[i] == pattern[j])
{
next[i] = next[j];
}
else
{
next[i] = j;
}
}
else
{
j = next[j];
}
}
i = 0;
j = 0;
while (content[i] && pattern[j])
{
if (content[i] == pattern[j])
{
++i;
++j;
}
else
{
j = next[j];
if (-1 == j)
{
++i;
++j;
}
}
}
free(next);
if (pattern[j])
{
return NULL;
}
else
{
return &content[i - j];
}
}
---------------------------------------------------------------------------
5. 编写反转字符串的程序,要求优化速度、优化空间。
#include <string.h>
void reverse_string(char * str)
{
int head;
int tail;
if (NULL == str)
{
return;
}
head = 0;
tail = strlen(str) - 1;
while (head < tail)
{
char tmp;
tmp = str[head];
str[head] = str[tail];
str[tail] = tmp;
++head;
--tail;
}
}
---------------------------------------------------------------------------
6. 在链表里如何发现循环链接?
#include <stddef.h>
struct listtype
{
int data;
struct listtype * next;
};
typedef struct listtype * list;
/* Check that whether there is loop in the singly linked list sll or not. */
int find_circle(list sll)
{
list fast = sll;
list slow = sll;
if (NULL == fast)
{
return -1;
}
while (fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return 1;
}
}
return 0;
}
参考:
http://ostermiller.org/find_loop_singly_linked_list.html
---------------------------------------------------------------------------
7. 给出洗牌的一个算法,并将洗好的牌存储在一个整形数组里。
---------------------------------------------------------------------------
8. 写一个函数,检查字符是否是整数,如果是,返回其整数值。 (或者:怎样只用 4 行代码编写出一个从字符串到长整形的函数?)
#include <ctype.h>
long strtol(const char * str)
{
int sign = 1;
long value;
if ('+' == *str)
{
++str;
}
else if ('-' == *str)
{
sign = -1;
++str;
}
value = 0;
while (*str)
{
if (!isdigit(*str))
{
return -1;
}
else
{
value = value * 10 + *str - '0';
}
++str;
}
return value * sign;
}
---------------------------------------------------------------------------
9. 给出一个函数来输出一个字符串的所有排列。
#include <stdio.h>
void permutation(char * p_str, char * p_begin)
{
if(!p_str || !p_begin)
{
return;
}
/*
* If p_begin points to the end of string,
* this round of permutation is finished,
* print the permuted string.
*/
if('/0' == *p_begin)
{
printf("%s/n", p_str);
}
/* Otherwise, permute string. */
else
{
char * p_ch;
for(p_ch = p_begin; *p_ch != '/0'; ++p_ch)
{
char temp;
/* Swap p_ch and p_begin. */
temp = *p_ch;
*p_ch = *p_begin;
*p_begin = temp;
permutation(p_str, p_begin + 1);
/* Restore p_ch and p_begin. */
temp = *p_ch;
*p_ch = *p_begin;
*p_begin = temp;
}
}
}
int main(int argc, char * argv[])
{
if (argc > 1)
{
permutation(argv[1], argv[1]);
}
return 0;
}
参考:http://zhedahht.blog.163.com/blog/static/254111742007499363479/
---------------------------------------------------------------------------
10. 请编写实现 malloc 内存分配函数功能一样的代码。
---------------------------------------------------------------------------
11. 给出一个函数来复制两个字符串 A 和 B。字符串 A 的后几个字节和字符串 B 的前几个字节重叠。
---------------------------------------------------------------------------
12. 怎样编写一个程序,把一个有序整数数组放到二叉树中?
---------------------------------------------------------------------------
13. 怎样从顶部开始逐层打印二叉树结点数据?请编程实现。
---------------------------------------------------------------------------
14. 转置单向链表 (也就是反序,注意链表的边界条件并考虑空链表)。
#include <stddef.h>
struct listtype
{
int data;
struct listtype * next;
};
typedef struct listtype * list;
/* Reverse the singly linked list *psll. */
void reverse_singly_linked_list(list * psll)
{
list h = NULL;
list p = *psll;
if (!psll || !*psll)
{
return;
}
while (p)
{
list tmp = p;
p = p->next;
tmp->next = h;
h = tmp;
}
*psll = h;
}
---------------------------------------------------------------------------
15. 将以空格为分隔符分隔的字符串逆序打印,但单词不逆序。例如“Hello world Welcome to China”的打印结果为“China to Welcome world Hello”。
#include <stdio.h>
#include <string.h>
/* Print string str partially, from start to end-1. */
void print_word(const char * str, int start, int end)
{
int i;
for (i = start; i < end; ++i)
{
printf("%c", str[i]);
}
}
/* Reversely print string str. */
void reversely_print_string(const char * str)
{
int len, i, end;
len = strlen(str);
end = len;
for (i = len - 1; i >= 0; --i)
{
if (' ' == str[i])
{
/* Print the word after this space. */
print_word(str, i + 1, end);
/* Print the space. */
printf("%c", str[i]);
/* Set the right boundary of the previous word. */
end = i;
}
}
/* Print the first word. */
print_word(str, 0, end);
}
int main(int argc, char * argv[])
{
if (argc > 1)
{
reversely_print_string(argv[1]);
}
printf("/n");
return 0;
}
---------------------------------------------------------------------------
16. 有 N 个数,这个 N 很大, 如有 100000 个,从这 N 个数中找出最大的 M 个数 (M << N),它的复杂度大概为多少?
法壹:
采用堆排序的方法。
1) 先建大顶堆,时间 O(N),然后 M 次取堆顶元素并维护堆,时间为 O(MlogN)。因此总的时间复杂度为 O(N + MlogN)。如果 M 为常数,则总的时间复杂度为 O(N)。
2) 如果 N 很大,不能一次载入内存。则我们可以先读入 M 个数,然后在这 M 个数基础上建小顶堆,时间 O(M)。然后对剩下的 N-M 个数,每读一个,将其与堆顶元素比较:若小等,读下一个;若大,则将其替换堆顶元素,然后维护一次堆,时间为 O(logM)。因此,最坏情况下时间复杂度为 O(M + N-M + (N-M)logM) = O(N + (N-M)logM)。若 M 为常数,则总的时间复杂度也为 O(N)。
比较 1 和 2:因为 M << N,因此 1 要快于 2。
法贰:
采用快速排序的方法。不断的划分后半部分 (比某个数大的部分),直到这个部分的数量小等于 M。如果后面部分已不足 M 个元素,则在前一部分找不够的个数以补足 M 个。如此循环直到找到 M 个。
也可以在后面部分个数小到一定程度时,比如小于 10M,进行插入排序,找最大的 M 个。
这个方法的时间复杂度不理想,最坏情况下会是 O(N^2)。
法叁:
1) 从 N 个数中取出 M 个,以插入排序法,按照从小到大的顺序放入数组 arr。
2) 从剩余的 N-M 个数中,依次取出每一个数和 arr[0] 比较,如大于 arr[0], 则删除 arr[0], 采用插入排序将此数插入到数组。
复杂度分析:步骤 1 花费时间 O(M^2);步骤 2 代价分为两部分:和 arr[0] 比较以及插入到数组 arr。对于前者,最好情况,最坏情况总是一样的,计作 O(N-M). 对于后者,虽然每次插入的代价不是固定的,但和 N 无关,平均和最快情况都是 O(M^2),故这部分时间复杂度是 O((N-M)M^2)。总的复杂度是 O((N-M)M^2)。
参考:
1) http://blog.csdn.net/patriotlml/archive/2006/09/09/1199793.aspx
2) http://topic.csdn.net/t/20061031/14/5122253.html
3) http://topic.csdn.net/t/20051004/13/4307089.html
---------------------------------------------------------------------------