基础算法

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的次数相加。

——敬请期待,持续更新中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值