交换一次得到最大值
题目描述:
Given a non-negative integer, you could swap two digits at most once to get the maximum valued number. Return the maximum valued number you could get.
Example 1:
Input: 2736 Output: 7236 Explanation: Swap the number 2 and the number 7.
Example 2:
Input: 9973 Output: 9973 Explanation: No swap.
Note:
- The given number is in the range [0, 108]
刚开始看见题目,以为很简单,只要找到每位中的最大值,然后与最高位交换即可。,例如:2376,交换完后是:7326。代码(这是错误示范哈):
public int maximumSwap(int num) {
if(num<10){
return num;
}
int result=0;
int n=10;
int max=num%n;
int len=0;
int index=0;
int[] numArr=new int[20];
numArr[0]=max;
while(num/n!=0){
num=num/n;
len++;
numArr[len]=num%n;
if(max<numArr[len]){
max=numArr[len];//记录最大值
index=len; //记录最大值的位置
}
}
numArr[index]=numArr[len]; //将最高位复制给最大值所在的位置
numArr[len]=max; //将最大值赋值给最高位
for(int i=0;i<=len;i++){
System.out.println(numArr[i]);
result=result+numArr[i]*(int)Math.pow(10,i);
}
return result;
}
可是针对9362这样的整数最高位已经是最大了,还是可以交换得到更大值:9632。
主要思路:就是把大数换到前面去,所以还是从头开始遍历,一旦发现可以交换的值就结束了。
改进:遍历每一位去执行刚刚的算法,即:若第一位已经是最大值,则第一位无需交换;接下来判断第二位与剩余位相比是否是最大值,若不是,则记录最大值的位置,与第二位交换;若是,则判断下一位;直至最后一位。得到代码如下(还是错误示范):
public int maximumSwap(int num) {
if(num<10){
return num;
}
String numStr=num+"";
char[] numArr=numStr.toCharArray(); //将数字转换为字符数组
int swapIndex=0; //记录被交换值的索引位置
char swapValue='a'; //记录被交换的值
for(int i=0;i<numArr.length;i++){ //遍历每一位是否是最大值
swapValue=numArr[i];
swapIndex=i;
for(int j=i+1;j<numArr.length;j++){ //遍历当前位后面的每一位,出现大于它的值就更新,并记录这个最大值的位置
if(numArr[i]<numArr[j]){
numArr[i]=numArr[j];
swapIndex=j;
}
}
if(i!=swapIndex){ //若没有比当前位大的值,则swapIndex等于i, 在每次循环前已经赋值啦 否则出现了可以替换的值,结束循环
break;
}
}
numArr[swapIndex]=swapValue;//在前面已经对判断位赋值了,所以这里仅需要对被交换位赋值就可以啦
numStr=String.valueOf(numArr);
return Integer.valueOf(numStr); //转换为整数
}
这里面出现的问题是:不仅要考虑大数往前移,还有考虑小数要往后移,尤其是出现重复数字时,该情况尤为明显,例如:1939,按照刚刚的算法,判断最高位1的时候,就输出交换结果9139,但是如果将1与最后一位9交换可以得到更大值,即:9931。
改进:判断的时候加个等号,这样更新最后的索引就是最后面的索引值了,例如第一次交换后当前位为9,下一次与3判断是大于3的不做处理,下一次与9判断是等于,这时候要更新swapIndex的值,让它为最后一位9的索引值,这样就可以把最小值换到后面。代码如下(结果依旧不对...骂人不对):
public int maximumSwap(int num) {
if(num<10){
return num;
}
String numStr=num+"";
char[] numArr=numStr.toCharArray();
int swapIndex=0;
char swapValue='a';
for(int i=0;i<numArr.length;i++){
swapValue=numArr[i];
swapIndex=i;
for(int j=i+1;j<numArr.length;j++){
if(numArr[i]<=numArr[j]){
numArr[i]=numArr[j];
swapIndex=j;
}
}
if(i!=swapIndex){
break;
}
}
numArr[swapIndex]=swapValue;
numStr=String.valueOf(numArr);
return Integer.valueOf(numStr);
}
这时候出现的尬值就是:98368,正常交换后是:98863.然而由于刚刚程序里面加了等号,所以遇见相等的也交换了,直接跳出循环返回结果。这是不对的。所以要对等于的情况进行特殊处理。
加个判断值,判断当前值是否被交换过,如果被交换过,那么等于是有意义的,如果没有被交换过,那么等于就没有任何用。
public int maximumSwap(int num) {
if(num<10){
return num;
}
String numStr=num+"";
char[] numArr=numStr.toCharArray();
int swapIndex=0;
char swapValue='a';
boolean isSwap=false;
for(int i=0;i<numArr.length;i++){
swapValue=numArr[i];
swapIndex=i;
isSwap=false;
for(int j=i+1;j<numArr.length;j++){
if(numArr[i]<numArr[j]){
numArr[i]=numArr[j];
swapIndex=j;
isSwap=true;
}else if(numArr[i]==numArr[j]){
swapIndex=j;
}
}
if((i!=swapIndex)&&isSwap){
break;
}
}
numArr[swapIndex]=swapValue;
numStr=String.valueOf(numArr);
return Integer.valueOf(numStr);
}
终于成功,撒花~
下面再分析一下,LeetCode上的更好的代码:
public int maximumSwap(int num) {
char[] digits = Integer.toString(num).toCharArray(); //一样转为了字符数组
int[] buckets = new int[10]; // 桶 值 存储 0-9分别在该数中第几位
for(int i = 0;i < digits.length;i ++){
buckets[digits[i] - '0'] = i; //如果是6273的情况 则桶值是 [0,0,1,3,0,0,0,2,0,0]
}
for(int i = 0;i < digits.length;i ++){ //从第一位开始判断
for(int k = 9;k > digits[i] - '0';k --){ //从9开始判断是否比当前位大,如果大则判断在桶值中有没有,如果有则交换当前位与这个值
if(buckets[k] > i){
char temp = digits[i];
digits[i] = digits[buckets[k]];
digits[buckets[k]] = temp;
return Integer.valueOf(new String(digits));
}
}
}
return num;
}