• 剑指offer---3


    1.反转单链表,输入链表的头节点,输出该链表,并输出反转后的头节点

    这个题目不用再说了,写过N边了

    SLnode reverse(SLnode head)
    {
        SLnode reverse_head = NULL;
        SLnode pnode = head->next;
        SLnode prev = NULL;
        SLnode pnext = NULL;
        while(pnode != NULL)
        {   
            pnext = pnode->next;
            if(pnext == NULL)
                reverse_head = pnode;
            pnode->next = prev;
            prev = pnode;
            pnode = pnext;
        }   
        head->next = reverse_head;
        return head;
    }

    以前自己还写过一段代码,两种方式的思路不同,这种是从第一个节点开始,把它的next指针指向它的前一个节点,直到最后一个节点为止

    下一种也是从第一个节点开始,但是把它的下一个节点放到它的前面,比较不容易理解

    SLnode reverse(SLnode head)
    {
        if(head == NULL || head->next == NULL)
            return head;
    
        SLnode p = NULL;
        SLnode tmp = head->next;
        while(tmp->next != NULL)
        {
            p = tmp->next;
            tmp->next = p->next;
            p->next = head->next;
            head->next = p;
        }
        return head;
    }


    2.合并排序的链表,使合并后仍然递增,这个也是写了很多边了,递归和非递归实现

    SLnode mergelist2(SLnode head1, SLnode head2)
    {
        if(head1 == NULL)
            return head2;
        if(head2 == NULL)
            return head1;
        SLnode MergerHead = NULL;
        if(head1->data > head2->data)
        {
            MergerHead = head1;
            MergerHead->next = mergelist2(head1->next, head2);
        }
        else
        {
            MergerHead = head2;
            MergerHead->next = mergelist2(head1, head2->next);
        }
        return MergerHead;
    }

    再一次强调递归和非递归的本质区别,一个是使用系统提供的栈,即函数参数压栈,一个是我们自己使用栈,此处的非递归采用的是迭代,某种程度上来说,也是使用的栈

    SLnode mergelist(SLnode head1, SLnode head2)
    {
    	if(head1 == NULL)
    		return head2;
    	if(head2 == NULL)
    		return head1;
    	SLnode node1 = head1->next;
    	SLnode node2 = head2->next;
    
    	SLnode mergehead = (SLnode)malloc(sizeof(SLnode));
    	
    	if(node1->data > node2->data)
    	{
    		mergehead->next = node1;
    		node1 = node1->next;
    	}
    	else
    	{
    		mergehead->next = node2;
    		node2 = node2->next;
    	}
    
    	SLnode temp = mergehead->next;
    	while(node1 != NULL && node2 != NULL)
    	{
    		if(node1->data > node2->data)
    		{
    			temp->next = node1;
    			node1 = node1->next;
    			temp = temp->next;
    		}
    		else
    		{
    			temp->next = node2;
    			node2 = node2->next;
    			temp = temp->next;
    		}
    	}
    	if(node1 != NULL)
    		temp->next = node1;
    	if(node2 != NULL)
    		temp->next = node2;
    
    	return mergehead;
    }
    


    3.输入两颗二叉树,判断B是不是A的子结构

    这个题目很有思想,我是看了思路之后才写出来的,需要分为两步考虑,首先从A中查找是否有B的根节点,如果找到了,再比较A的左子树和B的左子树,A的右子树和B的右子树是否相

    等,如果找不到,则继续在A的左子树和右子树中查找是否有和B的根节点相同的,直到A的叶子节点为止

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct BitreeNode
    {
    	int data;
    	struct BitreeNode*left, *right;
    }BitreeNode;
    
    void construct_bitree(BitreeNode **root)
    {
    	int x;
    	scanf("%d",&x);
    
    	if(x != -1)
    	{
    		*root =  (BitreeNode*)malloc(sizeof(struct BitreeNode));
    		if(*root == NULL)
    			printf("malloc error
    ");
    		(*root)->data = x;
    		(*root)->left = NULL;
    		(*root)->right = NULL;
    
    		printf("input the left node, -1 to end
    ");
    		construct_bitree(&((*root)->left));
    
    		printf("input the right node, -1 to end
    ");
    		construct_bitree(&((*root)->right));
    	}
    }
    void print_tree(BitreeNode *root)
    {
    	if(root != NULL)
    	{
    		printf("%d
    ",root->data);
    		print_tree(root->left);
    		print_tree(root->right);
    	}
    }
    
    void Free(BitreeNode *root)
    {
    	if(root != NULL)
    		return;
    	else
    	{
    		Free(root->left);
    		Free(root->right);
    		free(root);
    	}
    }
    
    int find_subtree_core(BitreeNode *root1, BitreeNode *root2)
    {
    	if(root1 == NULL)
    		return 0;
    	if(root2 == NULL)
    		return 1;
    	if(root1->data != root2->data)
    		return 0;
    	return find_subtree_core(root1->left, root2->right)&&
    		find_subtree_core(root1->right, root2->right);
    }
    
    int find_subtree(BitreeNode *root1, BitreeNode *root2)
    {
    	int ret = 0;
    	if(root1 != NULL && root2 != NULL)
    	{	
    		if(root1->data == root2->data)
    			ret = find_subtree_core(root1, root2);
    		else
    		{
    			if(!ret) find_subtree(root1->left, root2);
    			if(!ret) find_subtree(root1->right, root2);
    		}
    	}
    	return ret;
    }
    
    void main()
    {
    
    	BitreeNode *root1 = NULL;
    	BitreeNode *root2 = NULL;
    
    	printf("please input the root1:
    ");
    	construct_bitree(&root1);
    	print_tree(root1);
    
    	printf("please input the root2:
    ");
    	construct_bitree(&root2);
    	print_tree(root2);
    
    	if(find_subtree(root1, root2))
    		printf("find the subtree
    ");
    
    	Free(root1);
    	Free(root2);
    }
    


    4.二叉树的镜像,要求输入一个二叉树,输出它的镜像

    思路:如果根节点的左右孩子节点存在,就交换根节点的左右孩子节点,如果左右孩子节点存在,再递归调用

    void mirror_of_bitree(BitreeNode *root)
    {
    	if(root == NULL || (root->left == NULL)&&(root->right == NULL))
    		return;
    	BitreeNode *temp = root->left;
    	root->left = root->right;
    	root->right = temp;
    
    	mirror_of_bitree(root->left);
    	mirror_of_bitree(root->right);
    }


    5.顺时针打印一个矩阵,如果输入的是

    1     2     3     4

    5     6     7     8

    打印的是1  2  3  4  8  7  6  5 

    这个题目我按照自己思路来解决的,没有按照书上给的思路,直接来想就是打印最外面一圈,然后缩小范围,打印里面一圈,直到范围内曾大小为0时为止

    #include <stdio.h>
    #define column 4
    #define row 4
    //这个程序是我自己写出来的,虽然比较臃肿和难以理解,但是思路很清晰
    void main()
    {
    	int a[row][column] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
    
    	int begin1 = 0, end1 = row-1;
    	int begin2 = 0, end2 = column-1;
    	int i = 0, j = 0;
    	while(i <= end1)
    	{
    		if(j > end2)
    			break;
    		for( ; j <= end2; j++)
    			printf("%d
    ",a[i][j]);
    		j--;
    		i++;
    		if(i > end1)
    			break;
    		for( ; i <= end1; i++)
    			printf("%d
    ",a[i][j]);
    		j--;
    		i--;
    		if(j < begin2)
    			break;
    		for( ; j >= begin2; j--)
    			printf("%d
    ",a[i][j]);
    		i--;
    		j++;
    		if(i <= begin1)
    			break;
    		for( ; i > begin1; i--)
    			printf("%d
    ",a[i][j]);
    		i++;
    		j++;
    		begin1++;begin2++;
    		end1--;end2--;
    	}
    }
    


    6.定一个栈的数据结构,并实现一个能够得到最小元素的min函数,调用min,pop,push函数的时间复杂度都是O(1)

    首先我们要明白这个题目的入手点在于用栈去实现这样的数据结构,我当时还傻不啦叽的自己去实现一个栈,找到了切入点,我们再来看怎么去实现这个函数,

    在栈中添加一个成员变量作为最小元素?那要是这个变量弹出了怎么办?所以,我们可以每次入栈元素的时候就添加一个变量,这个时候我们可以使用辅助栈,这里的关键在于

    每次入栈的时候我们要去更新辅助栈中元素,使它的栈顶是最小元素

    #include <iostream>
    #include <stack>
    #include <stdlib.h>
    using namespace std;
    
    template<class T>
    class Stack
    {
    private:
    	stack<T> m_data;
    	stack<T> m_min;
    public:
    	Stack();
    	~Stack();
    
    	void push(T data);
    	void pop();
    
    	const T& min() const;
    };
    template<class T>
    Stack<T>::Stack()
    {}
    
    template<class T>
    Stack<T>::~Stack()
    {}
    
    template<class T>
    void Stack<T>::push(T data)
    {
    	m_data.push(data);
    	if(!m_min.empty() && data > m_min.top())
    			m_min.push(m_min.top());//每次入栈都要考虑最小元素的入栈
    	else
    		m_min.push(data);
    }
    
    template<class T>
    void Stack<T>::pop()
    {
    	if(m_data.empty())
    	{
    		cout<<"the stack is empty"<<endl;
    		return;
    	}
    	m_data.pop();
    	m_min.pop();
    }
    
    template<class T>
    const T& Stack<T>::min() const
    {
    	return m_min.top();	
    }
    
    int main()
    {
    	Stack<int> SS;
    	SS.push(2);
    	SS.push(3);
    	SS.push(4);
    	int ret = SS.min();
    	SS.pop();
    	cout<<"min num "<<ret<<endl;
    
    	SS.push(1);
    	SS.push(8);
    	SS.pop();
    	SS.pop();
    	SS.push(7);
    	ret = SS.min();
    	cout<<"min num "<<ret<<endl;
    
    	return 0;
    }
    

    7.一个整数数组里面,除了两个数字外,其它的数字都出现了两次,写出程序找出这两个只出现一次的数字,时间复杂度为O(n),空间复杂度为O(1)

    这个题目还是曾经百度的笔试题目,思路是这样的:

    既然其它数字都出现了两次,那么所有的数字求异或之后,得到的值就是只出现一次的那两个数字的异或值,然后我们找到这个值的二进制的最右边的那个1,并以此为依据,将这个数

    组分为两部分,因为其它出现两次的数字在个各位上的值一样,所以这两部分就分出了这两个数字

    一下是我的参考代码:

    #include <stdio.h>
    
    int find_the_last1(int x)
    {
    	int n = 0;
    	x = x&~(x-1);
    	while((1 & x) == 0)
    	{
    		x>>1;
    		n++;
    	}
    	return n;
    }
    
    void num_appear_once(int a[], int n)
    {
    	int ret = 0, ret1 = 0, ret2 = 0, i;
    	for(i = 0; i < n; i++)
    		ret ^= a[i];
    	int m = find_the_last1(ret);
    	for(i = 0; i < n; i++)
    	{
    		if((a[i]>>m)&1)
    			ret1 ^= a[i];
    		else
    			ret2 ^= a[i];
    	}
    	printf("%d	%d
    ",ret1, ret2);
    }
    
    
    
    void main()
    {
    	int a[] = {1,2,3,3,2,5,6,1};
    	num_appear_once(a, 8);
    }
    

    类比去年小米校招的题目:在一个长度为n的整形数组a里,除了三个数字只出现一次外,其他的数字都出现了2次。请写程序输出任意一个只出现一次的数字,程序时间和空间复杂度越小越好。例如: a = {1,3,7,9,5,9,4,3,6,1,7},输出4或5或6

    这个题目的思路和上面是一样的,只不过是三个数字,所以在我们异或之后,我们可以两两异或,三个数的两两异或就可以分为两组,参考代码:

    // lowbit表示的是某个数从右往左扫描第一次出现1的位置
    int lowbit(int x)
    {
    	return x&~(x-1);
    }
    void find(int* a , int n)
    {
    	int i , xors;
    	xors = 0;
    	for(i = 0 ; i < n ; ++i)
    		xors ^= a[i];
    	// 三个数两两的异或后lowbit有两个相同,一个不同,可以分为两组
    	int fips = 0;
    	for(i = 0 ; i < n ; ++i)
    		fips ^= lowbit(xors ^ a[i]);//这一步是个关键
    	// 表示的是:flips=lowbit(a^b)^lowbit(a^c)^lowbit(b^c) 
    	int b;    // 假设三个只出现一次的其中一个数为b
    	b = 0;
    	for(i = 0 ; i < n ; ++i)
    	{
    		if(lowbit(xors ^ a[i]) == fips)
    			b ^= a[i];
    	}
    	// 成功找到三个数中一个数
    	cout<<b<<endl;
    }




  • 相关阅读:
    Android_程序未处理异常的捕获与处理
    instanceof关键字
    乐优商城项目爬坑
    [LeetCode]Isomorphic Strings
    [LeetCode]Contains Duplicate II
    [LeetCode]Valid Sudoku
    [LeetCode]Valid Anagram
    [LeetCode]Contains Duplicate
    [LeetCode]Single Number
    [LeetCode]Valid Number
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3356112.html
Copyright © 2020-2023  润新知