剑指offer-第二章数据结构(数组,字符串,链表,树,栈与队列)及例题

一、数组(最简单的数据结构)

定义:占据一块连续内存并按照顺序存储数据。创建时先指定大小,分配内存。

优点:时间效率高。实现简单的hash(下标为key,对应的数据为value)

缺点:空间效率差。如果我们只在数组中存一个数字,也先分配所有的内存。

扩展:动态数组(解决空间效率差),手段:扩容后,复制内容到新的数组,释放之前的内存。时间性能变差,因此,要尽量减少改变数组容量的次数。

数组与指针:声明一个数组时,数组名字也是一个指针,指向数组的第一个元素。

例题:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序,请完成一个函数,输入这样一个二维数组和一个整数,判断数组中是否含有整数。

思路:

写一个bool Find(int* matrix, int rows, int columns, int number)函数,将一个矩阵的右上角的值a和这个整数b进行对比.

if(a==b) return true;

else if (a>b) 去掉当前列,在剩下的列中进行查找。

else 去掉当前行,在剩下的行中进行查找。

#include <iostream>
using namespace std;
bool Find(int* matrix, int rows, int columns, int number)
{
    bool found = false;  

    if(matrix != NULL&& rows > 0 && columns > 0)
    {
        int row = 0;
        int column = columns - 1;
        while(row < rows && column >=0)
        {
            if(matrix[row * columns + column] == number)
            {
                found = true;
                break;
            }
            else if(matrix[row * columns + column] > number)
                -- column;
            else
                ++ row;
        }
    }  

    return found;
}
//========测试代码=============
void Test(char * testName, int * matrix, int rows, int columns, int number, bool expected)
{
    if(testName != NULL)
        printf("%s begins: ",testName);
    bool result=Find(matrix, rows, columns, number);
    if(result == expected)
        printf("Passed.\n");
    else
        printf("Failed.\n");
}
//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数在数组中
void Test1()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test1", (int*)matrix, 4, 4, 7, true);
}  

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数不在数组中
void Test2()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test2", (int*)matrix, 4, 4, 5, false);
}  

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数是数组中最小的数字
void Test3()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test3", (int*)matrix, 4, 4, 1, true);
}  

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数是数组中最大的数字
void Test4()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test4", (int*)matrix, 4, 4, 15, true);
}  

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数比数组中最小的数字还小
void Test5()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test5", (int*)matrix, 4, 4, 0, false);
}  

//  1   2   8   9
//  2   4   9   12
//  4   7   10  13
//  6   8   11  15
// 要查找的数比数组中最大的数字还大
void Test6()
{
    int matrix[][4] = {{1, 2, 8, 9}, {2, 4, 9, 12}, {4, 7, 10, 13}, {6, 8, 11, 15}};
    Test("Test6", (int*)matrix, 4, 4, 16, false);
}  

// 鲁棒性测试,输入空指针
void Test7()
{
    Test("Test7", NULL, 0, 0, 16, false);
}
void main()
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();
    Test7();
}

二.字符串

定义:若干字符组成的序列

c++/c与Java中字符串的对比:c/c++中每个字符串都是以‘\0‘作为结尾;Java中却不是这样的,Java中的一切都是对象,对象有长度,编译器可以确定输出的字符个数。

题目:请实现一个函数,把字符串中的每一个空格替换成‘%20‘,例如,输入为"we are happy",则输出为:"we%20are%20happy"

思路:要弄清楚两点:第一,在原串上面干,字符串的长度变长。第二,在新串上面干,自己分配足够地内存。

从头开始遍历字符串,在插入字符后,后面的字符会后移。因此我们采用从后面开始遍历。

先遍历一次字符串,统计字符串中空格的总数,计算替换后字符串的总长度。用指针p1和p2分别指向原字符串的尾部和替换后字符串的尾部。

#include <iostream>
using namespace std;

void ReplaceBlank(char string[],int length)
{
	if(string==NULL||length<=0)
		return ;
	int originalLength=0;
	int numberOfBlank=0;
	int i=0;
	while(string[i]!=‘\0‘)
	{
		++ originalLength;
		if(string[i]==‘ ‘)
			++numberOfBlank;
		++i;
	}
	int newLength=originalLength+numberOfBlank*2;
	if(newLength>length)
	{
		return;
	}
	int indexOfOriginal=originalLength;
	int indexOfNew=newLength;
	while(indexOfOriginal>=0&&indexOfOriginal<indexOfNew)
	{
		if(string[indexOfOriginal]==‘ ‘)
		{
			string[indexOfNew--]=‘0‘;
			string[indexOfNew--]=‘2‘;
			string[indexOfNew--]=‘%‘;
		}
		else
			string[indexOfNew--]=string[indexOfOriginal];
            --indexOfOriginal;
	}
}
void Test(char* testName, char string[], int length, char expected[])
{
    if(testName != NULL)
        printf("%s begins: ", testName);

    ReplaceBlank(string, length);

    if(expected == NULL && string == NULL)
        printf("passed.\n");
    else if(expected == NULL && string != NULL)
        printf("failed.\n");
    else if(strcmp(string, expected) == 0)
        printf("passed.\n");
    else
        printf("failed.\n");
}

// 空格在句子中间
void Test1()
{
    const int length = 100;

    char string[length] = "hello world";
    Test("Test1", string, length, "hello%20world");
}

// 空格在句子开头
void Test2()
{
    const int length = 100;

    char string[length] = " helloworld";
    Test("Test2", string, length, "%20helloworld");
}

// 空格在句子末尾
void Test3()
{
    const int length = 100;

    char string[length] = "helloworld ";
    Test("Test3", string, length, "helloworld%20");
}

// 连续有两个空格
void Test4()
{
    const int length = 100;

    char string[length] = "hello  world";
    Test("Test4", string, length, "hello%20%20world");
}

// 传入NULL
void Test5()
{
    Test("Test5", NULL, 0, NULL);
}

// 传入内容为空的字符串
void Test6()
{
    const int length = 100;

    char string[length] = "";
    Test("Test6", string, length, "");
}

//传入内容为一个空格的字符串
void Test7()
{
    const int length = 100;

    char string[length] = " ";
    Test("Test7", string, length, "%20");
}

// 传入的字符串没有空格
void Test8()
{
    const int length = 100;

    char string[length] = "helloworld";
    Test("Test8", string, length, "helloworld");
}

// 传入的字符串全是空格
void Test9()
{
    const int length = 100;

    char string[length] = "   ";
    Test("Test9", string, length, "%20%20%20");
}

int main(int argc, char* argv[])
{
    Test1();
    Test2();
    Test3();
    Test4();
    Test5();
    Test6();
    Test7();
    Test8();
    Test9();

    return 0;
}

相关例题:有两个排序的数组A1和A2,内存在A1的末尾有足够的多余的空间来容纳A2,请实现一个函数,把A2中地所有的数字插入到A1中并且所有的数字是排序的。

思路:同样是定义两个指针p1和p2,分别指向字数组A1和A2尾端,sp1和sp2,分别指向数组A1和A2的头部。并且设置一个指针p3指向合并后的字符串(即:A1数组)的尾端。

当满足p1>sp1和p2>sp2的同时。if(*p1>=*p2)*p3=*p1;else *p3=*p2;之后,当满足p1>sp1,*p3=*p1;当满足p2>sp2,*p3=*p2。

//合并数组
#include <stdio.h>
void mergaMatrix(int* matrix1,int* matrix2,
                 int lenofmtrx1,int lenofmtrx2,int sizeofmatrix1)
{
    if(sizeofmatrix1 != 0 && matrix1 != NULL && lenofmtrx1 !=0 
        &&  matrix2 != NULL && lenofmtrx2 != 0 ) 
    {
        int* pNewMatrix1 = matrix1 + lenofmtrx1 + lenofmtrx2 -1;
        int* pMatrix1 = matrix1 + lenofmtrx1 - 1;
        int* pMatrix2 = matrix2 +lenofmtrx2 - 1;
  
        while(pMatrix1 >= matrix1 && pMatrix2 >= matrix2)
        {
            if(*pMatrix1 >= *pMatrix2)
                *pNewMatrix1-- = *pMatrix1--;
            else
                *pNewMatrix1-- = *pMatrix2--;
        }
        while(pMatrix1 >= matrix1)
        {
            *pNewMatrix1-- = *pMatrix1--;
        }
        while(pMatrix2 >= matrix2)
        {
            *pNewMatrix1-- = *pMatrix2--;
        }
    }
    return;
}
  
//单元测试
void test(int* matrix1,int* matrix2,
          int lenofmtrx1,int lenofmtrx2,int sizeofmatrix1)
{
    if(matrix1 != NULL)
    {
        for( int i=0; i<lenofmtrx1;i++)
        {
            printf("%d ",*(matrix1+i));
        }
    }
    printf("\n");
    if(matrix2 != NULL){
        for( int i=0; i<lenofmtrx2;i++)
        {
            printf("%d ",*(matrix2+i));
        }
    }
    printf("\n");
    mergaMatrix(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
    for( int i=0; i<lenofmtrx1+lenofmtrx2;i++)
    {
        printf("%d ",*(matrix1+i));
    }
    printf("\n");
}
//一般情况
void test1()
{
    const int sizeofmatrix1 = 100;
    int lenofmtrx1 = 3;
    int matrix1[sizeofmatrix1] = {1,3,5};
    int lenofmtrx2 = 4;
    int matrix2[] = {2,4,6,8};
    test(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
}
//其中一个数组的书全部小于另外一个
void test2()
{
    const int sizeofmatrix1 = 100;
    int lenofmtrx1 = 3;
    int matrix1[sizeofmatrix1] = {1,3,5};
    int lenofmtrx2 = 4;
    int matrix2[] = {6,7,8,9};
    test(matrix1,matrix2,lenofmtrx1,lenofmtrx2,sizeofmatrix1);
}
//其中一个为空
void test3()
{
    const int sizeofmatrix1 = 100;
    int lenofmtrx1 = 3;
    int matrix1[sizeofmatrix1] = {1,3,5};
    test(matrix1,NULL,lenofmtrx1,0,sizeofmatrix1);
}
//两个都为空
void test4()
{
    const int sizeofmatrix1 = 100;
    test(NULL,NULL,0,0,sizeofmatrix1);
}
int main()
{
    test1();
    test2();
    test3();
    test4();
    return 0;
}

三.链表(创建,尾部添加,删除,打印(从头到尾),打印(从尾到头(stack),打印从尾到头(递归(会导致栈溢出))面试中最频繁的数据结构。

定义:由指针把若干个节点链接成链状的结构。

例题:从尾到头打印一个链表。

思路:

1.创建链表返回头指针ListNode* createLink(int a[],int k)(初始化头指针Head和行动指针p,当满足当前长度小于链表的长度时。创建一个节点pNew并赋初值,if(Head==NULL){Head=pNew;p=pNew;}else{p->next=pNew;p=p->next;})。

2.尾部添加无返回值void addTail(ListNode** Head,int value)(用到了指针的指针ListNode**head,创建一个节点pNew并赋初值。if(*Head==NULL){*Head=pNew;else创建行动指针p并指向头结点,找到插入节点的头一个接点,在它的后面插入节点。})。

3.删除一个已知值的节点返回空,void removeNode(ListNode** Head,int value)(同样用到指针的指针,if(Head==NULL&&*Head==NULL)初始化要删除的指针toBeDelete,if(*Head==value){toBeDelete=*Head,*Head=*Head->next)},else,初始化行动指针p并指向头指针,找到要删除的节点的头一个节点p,if(p->next!=null&&p->next->value==value){ toBeDelete=p->next,p-next=p->next->next}。)

4.打印(从头到尾)返回空,void printLink(ListNode* pHead)(初始化行动指针p并指向头节点,当满足p不为空的时候,输出p->value,并且移动p=p->next)

5.打印(从尾到头(栈))返回空,void printLinkReservese(ListNode* pHead)(定义一个栈 std::stack<ListNode *>nodes,初始化行为指针p并指向头节点,当满足p不为空时,将nodes.push(p),并且移动p=p->next;当nodes.empty不为空时,将行为指针指向顶并且输出p->value,nodes.pop();)

6.打印(从尾到头(递归)),返回空。void printLinkReservese_Recurse(ListNode *pHead)(if(pHead!=NULL){if(pHead->next!=NULL) printLinkReservse_Reservese_Recurse(pHead->next);}输出pHead->value.)

#include<iostream.h>
#include <stdio.h>
#include <stack>
struct ListNode
{
	int m_nValue;
	ListNode* m_pNext;
};
void RemoveNode(ListNode ** pHead,int value)
{
	if(pHead==NULL||*pHead==NULL)
        return;
	ListNode* toBeDelete=NULL;
	if((*pHead)->m_nValue==value)
	{
		toBeDelete=*pHead;
		*pHead=(*pHead)->m_pNext;
	}
	else
	{
		ListNode*p=*pHead;
		while(p->m_pNext!=NULL&&p->m_pNext->m_nValue!=value)
			p=p->m_pNext;
		if(p->m_pNext!=NULL&&p->m_pNext->m_nValue==value)
		{
			toBeDelete=p->m_pNext;
			p->m_pNext=p->m_pNext->m_pNext;
		}
		 //这一步很重要,防止操作已经释放的内存空间
		if(toBeDelete!=NULL)
		{
			delete toBeDelete;
			toBeDelete=NULL;
		}
	}
}
void AddToTail(ListNode ** pHead,int value)
{
    ListNode * pNew=new ListNode();
    pNew->m_nValue=value;
    pNew->m_pNext=NULL;  

    if(*pHead==NULL)
    {
        *pHead=pNew;
    }
    else
    {
        ListNode * pNode = *pHead;
        while(pNode->m_pNext != NULL)
            pNode=pNode->m_pNext;
        pNode->m_pNext=pNew;
    }
}  

ListNode * CreateLink(int a[],int k)
{
    ListNode * Head=NULL,*q=NULL;
    for(int i=0;i<k;i++)
    {
        ListNode * pNew=new ListNode();
        pNew->m_nValue=a[i];
        pNew->m_pNext=NULL;  

        if(Head==NULL)
        {
            Head=pNew;
            q=pNew;
        }
        else
        {
            q->m_pNext=pNew;
            q=q->m_pNext;
        }
    }
    return Head;
}
//从头到尾打印列表
void printLink(ListNode * pHead)
{
    ListNode *p=pHead;
    while(p)
    {
        cout<<p->m_nValue<<" ";
        p=p->m_pNext;
    }
    cout<<endl;
}
//从尾到头打印链表(用栈)。
void PrintListReversesingly_Iteratively(ListNode * pHead)
{
	std::stack<ListNode *> nodes;
	ListNode *p=pHead;
	while(p)
	{
		nodes.push(p);
		p=p->m_pNext;
	}
	while(!nodes.empty())
	{
		p=nodes.top();
		cout<<p->m_nValue<<" ";
		nodes.pop();
	}
}
//从尾到头打印链表(用递归,隐式调用栈)。
void printListReversesingly_Recursively(ListNode * pHead)
{
	if(pHead!=NULL)
	{
		if(pHead->m_pNext!=NULL)
		{
			printListReversesingly_Recursively(pHead->m_pNext);
		}
	}
	cout<<pHead->m_nValue<<" ";

}

void main()
{
    int a[]={1,2,3};
    ListNode * Head=CreateLink(a,3);
    printLink(Head);  

    AddToTail(&Head,6);
    printLink(Head);
	printListReversesingly_Recursively(Head);
    cout<<endl;
    RemoveNode(&Head,3);

	printLink(Head);
	PrintListReversesingly_Iteratively(Head);
	 cout<<endl;

}

四.树(二叉树(创建,打印,删除))

定义:除了根节点之外,每个结点都有一个父节点,除了叶子节点外所有的节点都有一个或者多个子节点。

二叉树:每个节点最多有两个叶子节点

遍历:按照某个顺序访问树中的所有节点。

三种常见的遍历:前序遍历,中序遍历,后续遍历(可以用递归和循环两种方式实现)

可实现的题目:二叉树的深度,树的子结构,二叉树的后续遍历。从上到下遍历二叉树(宽度优先遍历)。

二查搜索树:左子结点总是小于等于根节点,而右子节点总是大于等于根节点。找到一个节点的平均时间为O(logn)

二叉树特例:堆和红黑二叉树堆:最大堆和最小堆。可以解决(快速找最大值和最小值问题)

红黑树:将树中的节点定义为红、黑两种颜色,通过规则确保从根节点到叶子节点的最长路径不超过最短路径的两倍。在C++ 中的STL(Standard Template libary(标准模板库),set,multiset,map,multimap等数据结构都是基于红黑树实现的。)

重建二叉树

题目:输入某个二叉树的前序遍历和中序遍历的结果,重建该二叉树,假设输入前序和中序遍历的结果中没有重复的数字。例如:输出得 前序遍历为{1,2,4,7,3,5,6,8}和中序遍历{4,7,2,1,5,3,8,6},重建二叉树。

思路:

1.创建二叉树,返回值为BinaryTreeNode *,BinaryTreeNode* construct(int* preorder,int* inorder,int length)(如果前序遍历和中序遍历的头指针都不空,及length大于0,则调用函数BinaryTreeNode *ConstructCore(int*startPreorder,int*endPreorder,int*startInorder,int*endInorder)(首先将前序遍历的第一个值赋值给rootValue,初始化根节点,if(startPreorder==endPreorder){if(startInorder==endInorder&&*startPreorder==*startInorder) return root;else 抛空异常。) 据中序遍历找到根节点。定义一个行动指针指p向中序遍历的头结点。当满足p<=endInorder时,找到根节点(root),分别求出左、右子树的长度leftLength和rightLength,当leftLength和rightLength不为零时,分别创建左右子树。root->left=调用ConstructCore函数,root->right=调用ConstructCore函数。

2.打印二叉树。无返回值。首先创建要打印一个节点的函数void printTreeNode(BinaryTreeNode* pNode)(当pNode不为空时,打印pNode的值,如果pNode->left为空,打印pNode->left->value,右指针同左指针一样操作。else 抛空。),然后再创建一个函数,返回值为空。void PrintTree(BinaryTreeNode *root){如果根节点不 为空打印根节点的值,左指针不为空,递归打印左子树。右指针同理。}。

3.删除二叉树。无返回值。函数void DeleteNode(BinaryTreeNode* pRoot){如果pRoot不为空,则将左右指针分别赋值给一个新的指针,然后删除pRoot,并且置空。递归删除左右子树。}

#include <stdio.h>
#include <iostream>
using namespace std;
struct BinaryTreeNode
{
	int m_nValue;
	BinaryTreeNode* m_pLeft;
	BinaryTreeNode* m_pRight;

};
BinaryTreeNode* ConstructCore(int* startPreorder,int* endPreorder,int* startInorder,int* endInorder)
{
	int rootValue=startPreorder[0];
	BinaryTreeNode* root=new BinaryTreeNode();
	root->m_nValue=rootValue;
	root->m_pLeft=root->m_pRight=NULL;
	if(startPreorder==endPreorder)
	{
		if(startInorder==endInorder&&*startPreorder==*startInorder)
		{
			return root;
		}
	else
		throw std::exception("Invalid put!");
	}
	//通过中序遍历序列找到根节点
	int* rootInorder=startInorder;
	while(rootInorder<=endInorder&&*rootInorder!=rootValue)
	{
		++rootInorder;
	}
         if(rootInorder==endInorder&&*rootInorder!=rootValue)
	{
		throw std::exception("Invalid put");
	}
	int leftLength=rootInorder-startInorder;
	int rightLength=endInorder-rootInorder;
	int* leftPreorderEnd=startPreorder+leftLength;
	if(leftLength>0)
	{
	    //递归构建左子树
		root->m_pLeft=ConstructCore(startPreorder+1,leftPreorderEnd,startInorder,rootInorder-1);
	}
	if(rightLength>0)
	{
	    //递归构建右子树
		root->m_pRight=ConstructCore(leftPreorderEnd+1,endPreorder,rootInorder+1,endInorder);
	}
	return root;

}
BinaryTreeNode* Construct(int* preorder,int* inorder,int length)
{
	if(preorder==NULL||inorder==NULL||length<=0)
	{
	throw std::exception("Invalid put!");
	}
	return ConstructCore(preorder,preorder+length-1,inorder,inorder+length-1);
}

void PrintTreeNode(BinaryTreeNode* pNode)
{
    if(pNode != NULL)
    {
        printf("value of this node is: %d\n", pNode->m_nValue);  

        if(pNode->m_pLeft != NULL)
            printf("value of its left child is: %d.\n", pNode->m_pLeft->m_nValue);
        else
            printf("left child is null.\n");  

        if(pNode->m_pRight != NULL)
            printf("value of its right child is: %d.\n", pNode->m_pRight->m_nValue);
        else
            printf("right child is null.\n");
    }
    else
    {
        printf("this node is null.\n");
    }  

    printf("\n");
}
  //递归打印左右子树
void PrintTree(BinaryTreeNode* pRoot)
{
    PrintTreeNode(pRoot);  

    if(pRoot != NULL)
    {
        if(pRoot->m_pLeft != NULL)
            PrintTree(pRoot->m_pLeft);  

        if(pRoot->m_pRight != NULL)
            PrintTree(pRoot->m_pRight);
    }
}
  //递归删除左右子树
void DestroyTree(BinaryTreeNode* pRoot)
{
    if(pRoot != NULL)
    {
        BinaryTreeNode* pLeft = pRoot->m_pLeft;
        BinaryTreeNode* pRight = pRoot->m_pRight;  

        delete pRoot;
        pRoot = NULL;  

        DestroyTree(pLeft);
        DestroyTree(pRight);
    }
}  

void main()
{
    const int length = 8;
    int preorder[length] = {1, 2, 4, 7, 3, 5, 6, 8};
    int inorder[length] = {4, 7, 2, 1, 5, 3, 8, 6};  

    BinaryTreeNode *root = Construct(preorder, inorder, length);
    PrintTree(root);
    DestroyTree(root);
}

五.栈与队列

栈定义:用来存储函数调用时各个函数的参数,返回地址及临时变量等。

特点:先进后出,不考虑排序。

队列特点:先进先出。

题目:用两个栈实现一个队列,队列申明如下,实现它的两个函数appendTail和deleteHead,即:队列的尾部插入节点,头部删除节点。

思路:自定义一个函数模板CQueue,它有public属性或方法:构造函数,析构函数,在尾部添加节点void appendTail(const T& node),删除队列的头节点T deleteHead().私有属性:stack<T> stack1;stack<T>stack2;由于定义CQueue,为模板的原因,在尾部添加节点实现函数为:template< typename T> void cQueue<T>::appendTail(const T& node){stack1.push(node)},删除队列头部的节点template<T> T cQueue<T> ::deleteHead()如果stack2<=0,当stack1.size()满足大于0时,将stack1,中的所有元素压入stack2.如果stack2==0,抛空。定义一个T类型的head并且赋初值为stack2.top,stack2.pop(),返回Head。

#include <iostream>
#include <stack>
#include <exception>
using namespace std;  

template <typename T> class CQueue
{
public:
    CQueue(void);
    ~CQueue(void);  

    // 在队列末尾添加一个结点
    void appendTail(const T& node);  

    // 删除队列的头结点
    T deleteHead();  

private:
    stack<T> stack1;
    stack<T> stack2;
};  

//成员函数定义.
template <typename T>
CQueue<T>::CQueue(void)
{
}  

template <typename T>
CQueue<T>::~CQueue(void)
{
}  

//向队列中插入元素.
template <typename T>
void CQueue<T>::appendTail(const T& node)
{
    stack1.push(node);
}  

//从队列中删除元素.
template <typename T>
T CQueue<T>::deleteHead()
{
    //先判断stack2是否为空.
    //若其不为空,则从中弹出元素;
    //若为空,则将stack1中元素依次弹出并存入stack2中.
    if(stack2.size() <= 0)
        while (stack1.size() > 0)
        {//当stack2为空时,将stack1中所有元素存入stack2中.
            T data = stack1.top();
            stack1.pop();
            stack2.push(data);
        }
        if(stack2.size() == 0)
            throw new exception("queue is empty!");  

        T head = stack2.top();
        stack2.pop();  

        return head;
}  

//测试函数.
void Test(char actual, char expected)
{
    if(actual == expected)
        cout << "Test passed." << endl;
    else
        cout << "Test failed.\n" << endl;
}  

int main()
{
    CQueue<char> queue;  

    queue.appendTail(‘a‘);
    queue.appendTail(‘b‘);
    queue.appendTail(‘c‘);  

    char head = queue.deleteHead();
    Test(head, ‘a‘);  

    head = queue.deleteHead();
    Test(head, ‘b‘);  

    queue.appendTail(‘d‘);
    head = queue.deleteHead();
    Test(head, ‘c‘);  

    queue.appendTail(‘e‘);
    head = queue.deleteHead();
    Test(head, ‘d‘);  

    head = queue.deleteHead();
    Test(head, ‘e‘);  

    return 0;
}
时间: 2024-10-02 03:03:03

剑指offer-第二章数据结构(数组,字符串,链表,树,栈与队列)及例题的相关文章

数据结构 数组,链表,栈,队列理解

数据结构 数据结构是指相互之间存在一种或多种特定关系的数据元素的集合.再简单描述一下:数据结构就是描述对象间逻辑关系的学科. 数据存储结构 常用的数据存储方式有两种:顺序存储,非顺序存储.顺序存储就是把数据存储在一块联系的存储介质(硬盘或内存等)中.反之则是非顺序存储. Java中的数组就是典型的顺序存储,链表就是非顺序存储.数组存储数据时会开辟出一块联系内存,按顺序存储.链表先不会开辟出一块内存来,而是只需要知道下一个节点存储的位置,就能把所以的数据连起来了.所以单向链表的最后一个节点是指向N

剑指offer—第二章算法之快速排序

算法:排序和查找(二分查找,归并排序,快速排序),位运算等. 查找:顺序查找,哈希查找,二叉排序树查找,哈希表. 二分查找可以解决:"旋转数组中的最小数字","数字在排序数组出现的次数"等问题. 哈希表:优点:O(1)时间内查找一个元素的效率最高. 缺点:需要额外的空间来实现哈希表. 二叉排序树查找:对应二查搜索树查找,可以解决:"二叉收索树的后续遍历","二查搜索树和双向链表" 排序:排序算法要熟练,额外空间消耗,平均时间复

剑指Offer(书):用两个栈实现队列

题目:用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 分析:入栈时只入栈1,出栈为栈2:若栈2不为空,直接出栈:否则,将栈1中的值依次入栈2,之后栈2出栈 Stack<Integer> stack1 = new Stack<Integer>(); Stack<Integer> stack2 = new Stack<Integer>(); public void push(int node) { stack1.push(n

剑指OFFER之二维数组中的查找(九度OJ1384)

题目描述: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 输入: 输入可能包含多个测试样例,对于每个测试案例, 输入的第一行为两个整数m和n(1<=m,n<=1000):代表将要输入的矩阵的行数和列数. 输入的第二行包括一个整数t(1<=t<=1000000):代表要查找的数字. 接下来的m行,每行有n个数,代表题目所给出的m行n列的矩阵(矩阵如题目描述所示,每

【剑指offer】Q29:数组中出现次数超过一半的数字

就本题而言,个人觉得练习下partition函数是有必要的,毕竟它是快速排序的核心,是基础性的东西,也是必须要掌握的,至于书中给出的"取巧"性解法,是属于个人思维能力的考察,是一种考虑问题的思路,不是一两个问题就能练就的. partition函数,包括快速排序,是一定要信手拈来的,必须的. import random def MoreThanHalf(array): if len(array) == 0: return 0 start = 0 end = len(array) - 1

【剑指offer】二维数组中的查找

题目描述: 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 分析: 首先选择数组中右上角的数字.如果该数字等于要查找的数字,查找过程结束:如果该数字大于要查找的数字,剔除这个数字所在的列:如果该数字小于要查找的数字,剔除这个数字所在的行.依次类推,直到查找范围为空. 示例程序: #include <stdio.h> #include <stdlib.h> int

【剑指offer】Q40:数组中出现一次的数字

按着书里面讲述的方法,根据某一位来将整个数组拆分成两个部分,取每一部分中出现一次的数.书中的处理略显复杂,这里简化下分类的方法. def once(array): reOR = 0 for x in array: reOR ^= x bit1 = firstBit1(reOR) first = 0 second = 0 for x in array: if x & bit1 != 0: first ^= x else: second ^= x return first, second def f

【剑指offer】Q40:数组中出现一次的数

书里面关于分类的判断有些麻烦,通过某一位为0为1来对数组元素进行分类.假如第3位为1,那么也就是元素x & 8 等于或不等于0,所以没必要非的用第几位去判断. def once(array): reOR = 0 for x in array: reOR ^= x bit1 = firstBit1(reOR) first = 0 second = 0 for x in array: if x & bit1 != 0: first ^= x else: second ^= x return f

一起来刷《剑指Offer》——不修改数组找出重复的数字(思路及Python实现)

数组中重复的数字 在上一篇博客中<剑指Offer>-- 题目一:找出数组中重复的数字(Python多种方法实现)中,其实能发现这类题目的关键就是一边遍历数组一边查满足条件的元素. 然后我们在博客用最复杂的方式学会数组(Python实现动态数组)这篇博客中介绍了数组这一结构的本质,并自己动手实现了一个动态数组. 今天我们介绍一下另一道来自<剑指Offer>的关于数组的面试题--不修改数组找出重复的数字. 不修改数组找出重复的数字 题目二:不修改数组找出重复的数字 给定一个长度为 n+

剑指Offer对答如流系列 - 把数组排成最小的数

面试题45:把数组排成最小的数 题目描述 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个.例如输入数组{3, 32, 321},则打印出这3个数字能排成的最小数字321323. 问题分析 之前我们做过字符全排列的习题 剑指Offer对答如流系列 - 字符串的排列,但是将算法思想应用到这一题的话,效果不好,求出所有的组合,再计算出组合的最小值,这效率该多低啊. 我们还要进一步探究,看看有没有不错的规律,供我们使用. 因为数字拼接后的长度一样,拼接后的结果